Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e63b4afffd | |||
| e9f6529ead | |||
| e852a3c868 | |||
| 4c7d56f462 | |||
| 8ec7e54128 | |||
| eeb109e75a | |||
| 576db30b34 | |||
| 134dec5dbb | |||
| f327b06ed7 | |||
| 379ba26e85 | |||
| b5e8850de9 | |||
| 34de660c44 | |||
| db824bfc64 | |||
| 0e2af703bd | |||
| f17f0dac12 | |||
| cbcb2bd3a6 | |||
| 4e7e67c8b5 | |||
| 6db9c2630d | |||
| e0ede61e0f | |||
| 210a9a689b | |||
| 1f3eb68e48 | |||
| 6755925676 | |||
| d543a63e9b | |||
| 4103f30812 | |||
| ea322cd493 | |||
| 69225f9e14 | |||
| d77a936b32 | |||
| 357554265e | |||
| 0ef537cc6c | |||
| a8f4e84503 | |||
| 73d7e1161e | |||
| 6cd02b9714 | |||
| 76f00658f2 | |||
| e10fc255aa | |||
| 3377c54ecd | |||
| 4b2919c207 | |||
| 96cfe08558 | |||
| a4b86c5855 | |||
| a846fe815c | |||
| b64cc8f662 | |||
| 6251519591 | |||
| d2eab0d535 | |||
| b88da95b51 | |||
| d35209561d | |||
| 84c4d93ca5 | |||
| ab8be3658f | |||
| 0b4539caa1 | |||
| 78a673f526 | |||
| d46abc7ad4 | |||
| 0ef7655697 | |||
| 63feb5dabf | |||
| c812a59efc | |||
| 964e61143c | |||
| f7275fb796 | |||
| a16524151c | |||
| 4760ff3201 | |||
| 13f650037e | |||
| aaccaa5a45 | |||
| 0ab31f0822 | |||
| b542249f11 | |||
| 6f600f1a37 | |||
| b75fb79677 | |||
| 2f47b7edd9 | |||
| 9e3c4edd1d | |||
| 90e48213e0 | |||
| f9f33a9117 | |||
| 60cb9fc959 | |||
| b4798f76a3 | |||
| a2b2d08ddc | |||
| 20e297db3a | |||
| b11b8caf9d | |||
| bc672b44ea | |||
| 36f90d6a2d | |||
| a0ae476619 | |||
| 5696c848a6 | |||
| 0f95685c05 | |||
| ec182cbd60 | |||
| 21c4b65610 | |||
| fdf25720e0 | |||
| 35bfd03669 | |||
| b0b4b9a903 | |||
| 976e185a4a | |||
| 273886701b | |||
| 7a002470e6 | |||
| c7d15b12df | |||
| 41cc38aa6f | |||
| 7f9b7e64c3 | |||
| 5d7f5c3a79 | |||
| 7e6d96b192 | |||
| 1d1d6b6c36 | |||
| 91aeaae9a5 | |||
| a22d1851cc | |||
| fc4bf57f95 | |||
| 8076bc8999 | |||
| 1b76800448 | |||
| 6706bdc696 | |||
| b3b09bcfd8 | |||
| 4768cd2f26 | |||
| 4413036585 | |||
| 91fe973361 | |||
| 8b5c042dee | |||
| 6f9a3922ef | |||
| 83a0be1009 | |||
| 48e8a0c0c6 | |||
| b154f75523 | |||
| 9fc8e18eb3 |
@@ -1,3 +1,6 @@
|
||||
As a special exception, the authors give permission to link this program
|
||||
with the OpenSSL library and distribute the resulting binary.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
|
||||
@@ -1,3 +1,886 @@
|
||||
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)
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/msgs/fr.msg: Added missed entries (thanks to Sergei Golovan)
|
||||
* src/msgs/nl.msg: Likewise
|
||||
|
||||
* src/msgs/es.msg: New spanish translation (thanks to Badlop)
|
||||
|
||||
2004-07-11 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_last.erl: Supprot for storing status from latest
|
||||
unavailable presence (thanks to Sergei Golovan)
|
||||
* src/ejabberd_sm.erl: Likewise
|
||||
* src/ejabberd_c2s.erl: Likewise
|
||||
|
||||
* src/mod_vcard.erl: Minor update (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_register.erl: Added "access" option (thanks to Sergei
|
||||
Golovan)
|
||||
* src/mod_irc/mod_irc.erl: Likewise
|
||||
* src/ejabberd.cfg.example: Updated
|
||||
* src/win32/ejabberd.cfg: Likewise
|
||||
|
||||
* src/mod_privacy.erl: Fixed module stopping (thanks to Sergei
|
||||
Golovan)
|
||||
* src/mod_private.erl: Likewise
|
||||
|
||||
* src/gen_mod.erl: Added function get_module_opt/3 (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
* src/ejabberd_local.erl: Minor fix (thanks to Sergei Golovan)
|
||||
|
||||
* doc/guide.tex: Updated (thanks to Sergei Golovan)
|
||||
|
||||
2004-07-10 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_roster.erl: Removed superfluous include_lib line
|
||||
|
||||
* doc/guide.tex: Updated
|
||||
|
||||
* src/msgs/fr.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_irc/mod_irc.erl: Added handler for disco items requests
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_vcard.erl: Added option for JUD disabling (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
* src/mod_configure2.erl: Fixed module stopping (thanks to Sergei
|
||||
Golovan)
|
||||
* src/mod_last.erl: Likewise
|
||||
* src/mod_privacy.erl: Likewise
|
||||
* src/mod_register.erl: Likewise
|
||||
* src/mod_roster.erl: Likewise
|
||||
* src/mod_vcard.erl: Likewise
|
||||
|
||||
* src/jd2ejd.erl: Added emergency catches (thanks to Sergei
|
||||
Golovan)
|
||||
* src/mod_last.erl: Likewise
|
||||
|
||||
* src/ejabberd_sm.erl: Removed needless call to
|
||||
mod_disco:unregister_feature (thanks to Sergei Golovan)
|
||||
|
||||
* src/ejabberd_local.erl: Better support for mod_disco (thanks to
|
||||
Sergei Golovan)
|
||||
* src/mod_disco.erl: Likewise
|
||||
|
||||
* src/translate.erl: Suport for "default language" option (thanks
|
||||
to Sergei Golovan)
|
||||
* src/ejabberd_config.erl: Likewise
|
||||
* src/ejabberd_c2s.erl: Likewise
|
||||
* src/ejabberd.hrl: Added 'MYLANG' macros
|
||||
|
||||
* src/ejabberd.cfg.example: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* doc/guide.tex: Updated (thanks to Sergei Golovan)
|
||||
|
||||
2004-07-09 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/win32/ejabberd.cfg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
2004-07-07 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/Makefile.win32: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/Makefile.in: Added installation of ejabberd.cfg (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
* src/web/ejabberd_http.erl: Fixed support for HTTP/1.0 clients
|
||||
|
||||
2004-07-06 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* doc/guide.tex: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/ejabberd_auth.erl: Minor fix
|
||||
|
||||
* src/ejabberd_c2s.erl: Fixed sending of presence to own resources
|
||||
|
||||
2004-06-18 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Added configuration of listened
|
||||
ports
|
||||
* src/ejabberd_listener.erl: Added API for configuration of port
|
||||
listeners
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Fixed "Stop" button on node
|
||||
management page
|
||||
|
||||
2004-05-22 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/msgs/nl.msg: Dutch translation (thanks to Sander Devrieze)
|
||||
|
||||
* src/web/ejabberd_http.erl: Added options for enabling HTTP
|
||||
polling and admin interface
|
||||
* src/web/ejabberd_web.erl: Likewise
|
||||
* src/ejabberd.cfg.example: Updated
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Updated
|
||||
|
||||
* doc/guide.tex: Updated
|
||||
|
||||
2004-05-17 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_muc/mod_muc.erl: Added access rules for using serveice
|
||||
and creating rooms (thanks to Sergei Golovan)
|
||||
|
||||
* src/win32/ejabberd.nsi: Updated (thanks to Sergei Golovan)
|
||||
* src/win32/CheckUserH.ini: Likewise
|
||||
|
||||
* src/translate.erl: Search translations in priv_dir instead of
|
||||
lib_dir (thanks to Sergei Golovan)
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/ejabberd.cfg.example: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/**/Makefile.in: Updated (thanks to Sergei Golovan)
|
||||
* src/**/Makefile.win32: Likewise
|
||||
|
||||
2004-05-16 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Updated
|
||||
|
||||
2004-05-14 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Updated
|
||||
|
||||
2004-05-09 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Updated
|
||||
|
||||
* src/ejabberd_listener.erl: Added API for adding/removing
|
||||
listeners
|
||||
|
||||
2004-05-08 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* doc/guide.tex: Updated
|
||||
|
||||
* src/ejabberd_listener.erl: Now possible to specify interface on
|
||||
which one socket will be listened, also added another way to
|
||||
specify SSL options
|
||||
* src/ejabberd.cfg.example: Updated
|
||||
|
||||
2004-05-07 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Updated (thanks to Andrey
|
||||
Zamaraev)
|
||||
|
||||
2004-05-05 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_ctl.erl: Added command for listing all registered
|
||||
users
|
||||
|
||||
* src/ejabberd_ctl.erl: Bugfix, support for text-load and restore
|
||||
(thanks to Leif Johansson)
|
||||
|
||||
2004-05-04 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Updated
|
||||
|
||||
2004-05-01 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_http.erl: 'Accept-Language' header support
|
||||
* src/web/ejabberd_web_admin.erl: Likewise
|
||||
* src/msgs/ru.msg: Updated
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Send status code "201" on room
|
||||
creation
|
||||
|
||||
2004-04-27 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/translate.erl: Search translations directory in priv_dir
|
||||
instead of lib_dir (thanks to Sergei Golovan)
|
||||
|
||||
* src/**/Makefile.in: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/win32/: Win32 installer stuff (thanks to Sergei Golovan)
|
||||
|
||||
* src/**/Makefile.win32: Updated (thanks to Sergei Golovan)
|
||||
* src/configure.bat: Likewise
|
||||
* src/configure.erl: Likewise
|
||||
|
||||
* doc/guide.tex: Updated (thanks to Sergei Golovan)
|
||||
|
||||
2004-04-26 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Better design for administration
|
||||
interface (not completed) (thanks to Andrey Zamaraev)
|
||||
* src/web/ejabberd_http.erl: Updated
|
||||
* src/web/ejabberd_web.erl: Likewise
|
||||
|
||||
2004-04-17 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_http.erl: Increased receive buffer
|
||||
|
||||
* src/mod_irc/mod_irc_connection.erl: Support for "/quote" command
|
||||
|
||||
2004-04-15 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd.erl: Added searching of files in code:priv_lib
|
||||
(thanks to Sergei Golovan)
|
||||
* src/translate.erl: Likewise
|
||||
|
||||
* src/ejabberd_app.erl: Added "log_path" configuration parameter
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* src/**/Makefile.win32: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/**/*.c: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/configure.erl: Added writing of version to Makefile.inc
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* doc/guide.tex: Updated link to expat (thanks to Sergei Golovan)
|
||||
|
||||
2004-04-10 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/idna.erl: Support for IDNA (RFC3490)
|
||||
* src/ejabberd_s2s_out.erl: Likewise
|
||||
|
||||
2004-04-03 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/xml.erl: element_to_string/1 and crypt/1 now returns deep
|
||||
list
|
||||
* src/mod_muc/mod_muc_room.erl (add_message_to_history): Replaced
|
||||
string:len with lists:flatlength
|
||||
|
||||
2004-03-21 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* (all): Updated win32 stuff (thanks to Sergei Golovan)
|
||||
|
||||
* src/web/ejabberd_web.erl: Added interface for access rules
|
||||
configuration
|
||||
|
||||
2004-03-20 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* doc/guide.tex: Updated
|
||||
|
||||
* src/web/ejabberd_web.erl: Updated
|
||||
|
||||
* src/web/ejabberd_http.erl: Bugfix
|
||||
|
||||
2004-03-16 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_roster.erl: Bugfix
|
||||
|
||||
* src/ejabberd_s2s.erl: More verbose error handling
|
||||
|
||||
2004-03-15 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web.erl: Minor update
|
||||
|
||||
2004-03-14 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web.erl: Added interface for users listsing and
|
||||
statistics
|
||||
|
||||
2004-03-13 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web.erl: New interface for ACLs editing
|
||||
|
||||
* src/web/ejabberd_http_poll.erl: Fixed "Content-Type" header,
|
||||
"Set-Cookie" is included only in first response in session, added
|
||||
missed behaviour definition
|
||||
|
||||
* src/web/ejabberd_http.erl: "Content-Type" header now can be
|
||||
changed
|
||||
|
||||
2004-03-12 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web.erl: Experiments with web-interface
|
||||
|
||||
* src/configure.ac: Updated
|
||||
* src/Makefile.in: Likewise
|
||||
|
||||
2004-03-10 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_http.erl: Removed debugging output
|
||||
|
||||
* src/ejabberd_c2s.erl: Fixed processing of get_presence request
|
||||
(thanks to Mickael Remond)
|
||||
|
||||
2004-03-08 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Now private conferences are
|
||||
visible to admins and owners of this conference (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
* src/mod_muc/mod_muc.erl: More xml:lang support (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
* src/mod_vcard.erl: Better processing of EMAIL tag (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
* src/ejabberd_s2s_out.erl: Added "catch" to "open_socket" (thanks
|
||||
to Sergei Golovan)
|
||||
|
||||
2004-03-07 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_http_poll.erl: Completed
|
||||
|
||||
2004-03-06 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/: Support for HTTP Polling (JEP-0025) (almost complete)
|
||||
|
||||
2004-03-04 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/: Updated
|
||||
|
||||
2004-03-03 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/: Minor update
|
||||
|
||||
2004-03-02 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/: Small HTTP server and admin web-interface to ejabberd
|
||||
(not completed yet)
|
||||
* src/ejabberd_sup.erl: Added HTTP processes supervisor
|
||||
|
||||
* src/ejabberd_c2s.erl: Added API to ask presence (thanks to
|
||||
Mickael Remond)
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Updated date parser (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
* src/mod_muc/mod_muc.erl: Added error descriptions (thanks to
|
||||
Sergei Golovan)
|
||||
* src/mod_muc/mod_muc_room.erl: Likewise
|
||||
|
||||
* src/mod_vcard.erl: Fixed vCard tag (thanks to Sergei Golovan)
|
||||
* src/mod_irc/mod_irc.erl: Likewise
|
||||
* src/mod_pubsub/mod_pubsub.erl: Likewise
|
||||
|
||||
* src/jlib.hrl: Added macros for errors with <text/> (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
2004-02-26 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Updated error codes, removed
|
||||
trailing "-" in history. updated subject sending, added <title/>
|
||||
in configuration form (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_irc/mod_irc.erl: Added vCard, ejabberd:configure
|
||||
replaced with jabber:iq:register (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_configure.erl: Updated "xml:lang" usage, updated some
|
||||
messages (thanks to Sergei Golovan)
|
||||
* src/mod_configure2.erl: Likewise
|
||||
* src/mod_disco.erl: Likewise
|
||||
* src/mod_register.erl: Likewise
|
||||
* src/mod_vcard.erl: Likewise
|
||||
* src/mod_irc/mod_irc.erl: Likewise
|
||||
* src/mod_muc/mod_muc.erl: Likewise
|
||||
* src/mod_muc/mod_muc_room.erl: Likewise
|
||||
* src/mod_pubsub/mod_pubsub.erl: Likewise
|
||||
|
||||
* src/jlib.hrl: Added "lang" field in "iq" record (thanks to
|
||||
Sergei Golovan)
|
||||
* src/jlib.erl: Likewise
|
||||
|
||||
* src/ejabberd_c2s.erl: Updated to latest JEP-0078 (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
2004-02-18 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_sm.erl: Bugfix
|
||||
|
||||
2004-02-15 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Support for history management
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_stats.erl: Updated error codes (thanks to Sergei
|
||||
Golovan)
|
||||
* src/mod_irc/mod_irc.erl: Likewise
|
||||
|
||||
* src/mod_configure.erl: "jabber:iq:data" replaced with
|
||||
"ejabberd:config" namespace (thanks to Sergei Golovan)
|
||||
* src/mod_disco.erl: Likewise
|
||||
|
||||
2004-02-12 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Added <session/> to stream features
|
||||
|
||||
2004-02-10 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_irc/mod_irc.erl: Now uses "ejabberd:config" namespace
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_disco.erl: Fixed disco category and type (thanks to
|
||||
Sergei Golovan)
|
||||
* src/mod_pubsub/mod_pubsub.erl: Likewise
|
||||
|
||||
* src/jlib.hrl: Added "ejabberd:config" namespace (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
2004-01-27 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_ctl.erl: Added command for log reopening
|
||||
|
||||
2004-01-18 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_ctl.erl: Added commands for backup processing
|
||||
|
||||
* src/ejabberd_c2s.erl: Added processing of xml:lang according to
|
||||
latest XMPP-IM draft
|
||||
|
||||
* src/xml.erl: Added replace_tag_attr/3 function
|
||||
|
||||
* src/mod_roster.erl: Added auto-reply on incoming subscription
|
||||
request according to latest XMPP-IM draft
|
||||
|
||||
* src/mod_offline.erl: Added pop_offline_messages/1 function
|
||||
* src/ejabberd_c2s.erl: Updated sending of offline messages
|
||||
|
||||
2004-01-17 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Bugfix, updated error codes
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* src/jlib.hrl: Updated error codes (thanks to Sergei Golovan)
|
||||
|
||||
2004-01-11 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Fixed bind namespace
|
||||
|
||||
* src/ejabberd_ctl.erl: New module for ejabberd administration
|
||||
* tools/ejabberdctl: Shell script for ejabberd administration
|
||||
|
||||
* src/mod_vcard.erl: Copyright update
|
||||
|
||||
* src/ejabberd_service.erl: Now possible to specify access rules
|
||||
for service
|
||||
* src/ejabberd.cfg.example: Updated
|
||||
|
||||
2004-01-06 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_router.erl: Added monitoring of processess that
|
||||
serve domains
|
||||
|
||||
* src/ejabberd_app.erl: Bugfix
|
||||
|
||||
2004-01-03 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_router.erl (do_route/3): Slightly changed behaviour
|
||||
|
||||
2004-01-01 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_sm.erl (do_route/3): Minor fix
|
||||
|
||||
* src/ejabberd_sm.erl (route_message/3): Minor changes
|
||||
|
||||
2003-12-28 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_app.erl: Now possible to specify path to log file
|
||||
via "EJABBERD_LOG_PATH" environment variable
|
||||
|
||||
* src/translate.erl: Now possible to specify path to "msgs"
|
||||
directory via "EJABBERD_MSGS_PATH" environment variable
|
||||
|
||||
* src/ejabberd.erl: Added get_so_path/0 function
|
||||
* src/ejabberd_app.erl: Use ejabberd:get_so_path/0 to load .so
|
||||
* src/mod_irc/iconv.erl: Likewise
|
||||
* src/stringprep/stringprep.erl: Likewise
|
||||
|
||||
2003-12-24 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Presence probe now sended from full JID
|
||||
|
||||
* src/mod_roster.erl: Bugfix
|
||||
|
||||
2003-12-23 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Bugfix
|
||||
|
||||
2003-12-21 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_roster.erl: Added workaround for legacy gateways
|
||||
(passing of "subscribed" presence)
|
||||
|
||||
* src/ejabberd_sm.erl: Minor fix in subscription processing
|
||||
|
||||
2003-12-17 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/jlib.hrl: Added declaration of "iq" record
|
||||
* (all): Updated to use "iq" record
|
||||
|
||||
2003-12-14 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_local.erl: Replaced register_local_route to
|
||||
register_route
|
||||
* src/ejabberd_service.erl: Likewise
|
||||
* src/mod_echo.erl: Likewise
|
||||
* src/mod_vcard.erl: Likewise
|
||||
|
||||
* src/ejabberd_router.erl: Partially rewrited
|
||||
* src/ejabberd_sm.erl: Likewise
|
||||
|
||||
2003-12-13 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_s2s.erl: Partially rewrited
|
||||
|
||||
* src/mod_roster.erl: Removed debugging code
|
||||
|
||||
2003-12-12 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_s2s_out.erl: Bugfix
|
||||
|
||||
2003-12-11 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_roster.erl: Updated subscription handling to latest
|
||||
XMPP-IM draft
|
||||
|
||||
2003-12-06 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_s2s_in.erl: Changed timeout processing, bugfix
|
||||
|
||||
* src/ejabberd_s2s_out.erl: Changed timeout processing
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_muc/mod_muc.erl: Better i18n support, added support for
|
||||
<registered/> field in iq:register replies (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
* src/mod_register.erl: More i18n support (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
2003-12-02 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Bugfix in processing of connection
|
||||
replacement
|
||||
|
||||
2003-11-28 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Added workaround for some Java clients
|
||||
|
||||
2003-11-27 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* examples/mtr/ejabberd.cfg: Updatted (thanks to Marshall T. Rose)
|
||||
|
||||
* src/ejabberd_auth.erl: LDAP attribute that holds user ID now
|
||||
configurable
|
||||
* src/ejabberd.cfg.example: Updated
|
||||
|
||||
2003-11-26 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Fixed processing of presence probe from
|
||||
client
|
||||
|
||||
2003-11-23 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/cyrsasl_digest.erl: Bugfix (thanks to Sergei Golovan)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
admin tool
|
||||
mod_muc logging
|
||||
|
||||
admin interface
|
||||
users management
|
||||
statistics about each user
|
||||
|
||||
+786
-416
File diff suppressed because it is too large
Load Diff
+814
-431
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,50 @@
|
||||
#!/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;
|
||||
+33
-31
@@ -1,42 +1,44 @@
|
||||
% $Id$
|
||||
% jabber.dbc.mtview.ca.us
|
||||
|
||||
override_acls.
|
||||
|
||||
{acl, admin, {user, "mrose", "jabber.dbc.mtview.ca.us"}}.
|
||||
|
||||
{acl, jabberorg, {server, "jabber.org"}}.
|
||||
{acl, mrose, {user, "mrose", "jabber.dbc.mtview.ca.us"}}.
|
||||
|
||||
{access, announce, [{allow, admin},
|
||||
{deny, all}]}.
|
||||
{access, c2s, [{deny, blocked},
|
||||
{allow, all}]}.
|
||||
{access, c2s_shaper, [{none, admin},
|
||||
{normal, all}]}.
|
||||
{access, configure, [{allow, admin},
|
||||
{deny, all}]}.
|
||||
{access, disco_admin, [{allow, admin},
|
||||
{deny, all}]}.
|
||||
{access, muc_admin, [{allow, admin},
|
||||
{deny, all}]}.
|
||||
{access, register, [{deny, all}]}.
|
||||
{access, s2s_shaper, [{fast, all}]}.
|
||||
|
||||
|
||||
{shaper, normal, {maxrate, 1000}}.
|
||||
{auth_method, internal}.
|
||||
{host, "jabber.dbc.mtview.ca.us"}.
|
||||
{outgoing_s2s_port, 5269}.
|
||||
{shaper, normal, {maxrate, 1000}}.
|
||||
{shaper, fast, {maxrate, 50000}}.
|
||||
{welcome_message, none}.
|
||||
|
||||
|
||||
{access, disco_admin, [{allow, admin},
|
||||
{deny, all}]}.
|
||||
{listen, [{5222, ejabberd_c2s,
|
||||
[{access, c2s},
|
||||
{shaper, c2s_shaper}]},
|
||||
{5223, ejabberd_c2s,
|
||||
[{access, c2s},
|
||||
{shaper, c2s_shaper},
|
||||
{ssl, [{certfile, "/etc/openssl/certs/ejabberd.pem"}]}]},
|
||||
{5269, ejabberd_s2s_in,
|
||||
[{shaper, s2s_shaper}]}]}.
|
||||
|
||||
{access, configure, [{allow, admin}]}.
|
||||
|
||||
{access, c2s, [{deny, blocked},
|
||||
{allow, all}]}.
|
||||
|
||||
|
||||
{access, c2s_shaper, [{none, admin},
|
||||
{normal, all}]}.
|
||||
|
||||
{access, muc_admin, [{allow, admin}]}.
|
||||
|
||||
|
||||
{host, "jabber.dbc.mtview.ca.us"}.
|
||||
|
||||
{listen, [{5222, ejabberd_c2s, [{access, c2s},
|
||||
{shaper, c2s_shaper}]},
|
||||
{5223, ejabberd_c2s, [{access, c2s},
|
||||
{ssl, [{certfile, "/etc/openssl/certs/ejabberd.pem"}]}]},
|
||||
{5269, ejabberd_s2s_in, []}
|
||||
]}.
|
||||
|
||||
% This value (5569) is only for debugging, must be 5269
|
||||
{outgoing_s2s_port, 5269}.
|
||||
|
||||
{modules, [
|
||||
{mod_register, []},
|
||||
@@ -52,13 +54,13 @@ override_acls.
|
||||
% {mod_irc, []},
|
||||
{mod_muc, []},
|
||||
{mod_pubsub, []},
|
||||
{mod_time, [{iqdisc, no_queue}]},
|
||||
{mod_time, []},
|
||||
{mod_last, []},
|
||||
{mod_version, []}
|
||||
]}.
|
||||
|
||||
|
||||
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
% End:
|
||||
|
||||
+36
-22
@@ -6,28 +6,31 @@ CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
EXPAT_CFLAGS = @EXPAT_CFLAGS@
|
||||
ERLANG_CFLAGS= @ERLANG_CFLAGS@
|
||||
|
||||
EXPAT_LIBS = @EXPAT_LIBS@
|
||||
ERLANG_LIBS = @ERLANG_LIBS@
|
||||
|
||||
prefix = @prefix@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@ @EXPAT_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@ @EXPAT_LIBS@
|
||||
|
||||
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ stringprep
|
||||
|
||||
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@
|
||||
ERLSHLIBS = expat_erl.so
|
||||
|
||||
DESTDIR =
|
||||
|
||||
EJABBERDDIR = $(DESTDIR)/var/lib/ejabberd
|
||||
BEAMDIR = $(EJABBERDDIR)/beam
|
||||
BEAMDIR = $(EJABBERDDIR)/ebin
|
||||
PRIVDIR = $(EJABBERDDIR)/priv
|
||||
SODIR = $(PRIVDIR)
|
||||
SODIR = $(PRIVDIR)/lib
|
||||
MSGSDIR = $(PRIVDIR)/msgs
|
||||
LOGDIR = $(DESTDIR)/var/log/ejabberd
|
||||
ETCDIR = $(DESTDIR)/etc/ejabberd
|
||||
|
||||
all: all-recursive $(ERLSHLIBS) compile-beam
|
||||
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 \
|
||||
@@ -41,24 +44,35 @@ mostlyclean-recursive maintainer-clean-recursive:
|
||||
|
||||
|
||||
$(ERLSHLIBS): %.so: %.c
|
||||
gcc -Wall $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(LIBDIRS) \
|
||||
gcc -Wall $(CFLAGS) $(LDFLAGS) $(LIBS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) \
|
||||
-lexpat \
|
||||
-lerl_interface \
|
||||
-lei \
|
||||
-o $@ -fpic -shared \
|
||||
$(EXPAT_LIBS) $(EXPAT_CFLAGS) \
|
||||
$(ERLANG_LIBS) $(ERLANG_CFLAGS) \
|
||||
-o $@ -fpic -shared
|
||||
|
||||
install: all
|
||||
mkdir -p $(BEAMDIR)
|
||||
cp *.beam $(BEAMDIR)
|
||||
mkdir -p $(SODIR)
|
||||
cp *.so $(SODIR)
|
||||
mkdir -p $(MSGSDIR)
|
||||
cp msgs/*.msg $(MSGSDIR)
|
||||
install -d $(BEAMDIR)
|
||||
install -m 644 *.beam $(BEAMDIR)
|
||||
rm -f $(BEAMDIR)/configure.beam
|
||||
install -m 644 *.app $(BEAMDIR)
|
||||
install -d $(SODIR)
|
||||
install -m 644 *.so $(SODIR)
|
||||
install -d $(MSGSDIR)
|
||||
install -m 644 msgs/*.msg $(MSGSDIR)
|
||||
install -d $(ETCDIR)
|
||||
install -b -m 644 ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg
|
||||
install -d $(LOGDIR)
|
||||
|
||||
clean: clean-recursive
|
||||
clean: clean-recursive clean-local
|
||||
|
||||
clean-local:
|
||||
rm -f *.beam $(ERLSHLIBS)
|
||||
|
||||
distclean: distclean-recursive clean-local
|
||||
rm -f config.status
|
||||
rm -f config.log
|
||||
rm -f Makefile
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
|
||||
|
||||
+126
-22
@@ -1,38 +1,142 @@
|
||||
|
||||
include Makefile.inc
|
||||
|
||||
EXPAT_DIR="c:\progra~1\expat-1.95.6"
|
||||
ALL : build
|
||||
|
||||
ALL : expat_erl.dll
|
||||
REL=..\release
|
||||
EREL=$(REL)\ejabberd-$(EJABBERD_VERSION)
|
||||
EBIN_DIR=$(EREL)\ebin
|
||||
SRC_DIR=$(EREL)\src
|
||||
PRIV_DIR=$(EREL)\priv
|
||||
SO_DIR=$(PRIV_DIR)\lib
|
||||
MSGS_DIR=$(PRIV_DIR)\msgs
|
||||
WIN32_DIR=$(EREL)\win32
|
||||
DOC_DIR=$(EREL)\doc
|
||||
|
||||
NSIS_SCRIPT=win32\ejabberd.nsi
|
||||
NSIS_HEADER=win32\ejabberd.nsh
|
||||
|
||||
installer : $(NSIS_SCRIPT) $(NSIS_HEADER)
|
||||
makensis $(NSIS_SCRIPT)
|
||||
|
||||
$(NSIS_HEADER) : Makefile.inc
|
||||
echo !define OUTFILEDIR "..\$(REL)" >$(NSIS_HEADER)
|
||||
echo !define TESTDIR "..\$(REL)\ejabberd-$(EJABBERD_VERSION)" >>$(NSIS_HEADER)
|
||||
echo !define VERSION "$(EJABBERD_VERSION)" >>$(NSIS_HEADER)
|
||||
|
||||
release_clean :
|
||||
if exist $(REL) rd /s /q $(REL)
|
||||
|
||||
|
||||
release : build release_clean
|
||||
mkdir $(REL)
|
||||
mkdir $(EREL)
|
||||
mkdir $(EBIN_DIR)
|
||||
copy *.beam $(EBIN_DIR)
|
||||
@erase $(EBIN_DIR)\configure.beam
|
||||
copy *.app $(EBIN_DIR)
|
||||
mkdir $(PRIV_DIR)
|
||||
mkdir $(SO_DIR)
|
||||
copy *.dll $(SO_DIR)
|
||||
mkdir $(MSGS_DIR)
|
||||
copy msgs\*.msg $(MSGS_DIR)
|
||||
mkdir $(WIN32_DIR)
|
||||
copy win32\ejabberd.cfg $(EREL)
|
||||
copy $(SYSTEMROOT)\system32\libeay32.dll $(EREL)
|
||||
copy $(SYSTEMROOT)\system32\ssleay32.dll $(EREL)
|
||||
copy win32\ejabberd.ico $(WIN32_DIR)
|
||||
mkdir $(SRC_DIR)
|
||||
copy *.app $(SRC_DIR)
|
||||
copy *.erl $(SRC_DIR)
|
||||
copy *.hrl $(SRC_DIR)
|
||||
copy *.c $(SRC_DIR)
|
||||
mkdir $(SRC_DIR)\eldap
|
||||
copy eldap\eldap.* $(SRC_DIR)\eldap
|
||||
copy eldap\ELDAPv3.asn $(SRC_DIR)\eldap
|
||||
mkdir $(SRC_DIR)\mod_irc
|
||||
copy mod_irc\*.erl $(SRC_DIR)\mod_irc
|
||||
copy mod_irc\*.c $(SRC_DIR)\mod_irc
|
||||
mkdir $(SRC_DIR)\mod_muc
|
||||
copy mod_muc\*.erl $(SRC_DIR)\mod_muc
|
||||
mkdir $(SRC_DIR)\mod_pubsub
|
||||
copy mod_pubsub\*.erl $(SRC_DIR)\mod_pubsub
|
||||
mkdir $(SRC_DIR)\stringprep
|
||||
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 $(DOC_DIR)
|
||||
copy ..\doc\*.html $(DOC_DIR)
|
||||
copy ..\doc\*.png $(DOC_DIR)
|
||||
|
||||
SOURCE = expat_erl.c
|
||||
OBJECT = expat_erl.o
|
||||
DLL = expat_erl.dll
|
||||
|
||||
build : $(DLL) compile-beam all-recursive
|
||||
|
||||
all-recursive :
|
||||
cd eldap
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_irc
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_muc
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_pubsub
|
||||
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 ..
|
||||
|
||||
compile-beam :
|
||||
erl -s make all report -noinput -s erlang halt
|
||||
|
||||
CLEAN :
|
||||
-@erase expat_erl.obj
|
||||
-@erase vc60.idb
|
||||
-@erase expat_erl.dll
|
||||
CLEAN : clean-recursive clean-local
|
||||
|
||||
clean-local :
|
||||
-@erase $(OBJECT)
|
||||
-@erase $(DLL)
|
||||
-@erase expat_erl.exp
|
||||
-@erase expat_erl.lib
|
||||
-@erase expat_erl.pch
|
||||
-@erase *.beam
|
||||
|
||||
CPP=cl.exe
|
||||
CPP_PROJ=/nologo /ML /W3 /GX /O2 /I "$(ERLANG_DIR)\usr\include" /I "$(EI_DIR)\include" /I "$(EXPAT_DIR)\source\lib" /D "WIN32" /D "NDEBUG" /D "_USRDLL" /D "_MBCS" /Fpexpat_erl.pch /YX /FD /c
|
||||
clean-recursive :
|
||||
cd eldap
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_irc
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_muc
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_pubsub
|
||||
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 ..
|
||||
|
||||
.c.obj::
|
||||
$(CPP) @<<
|
||||
$(CPP_PROJ) $<
|
||||
<<
|
||||
distclean : release_clean clean
|
||||
-@erase $(NSIS_HEADER)
|
||||
-@erase Makefile.inc
|
||||
|
||||
LINK32=link.exe
|
||||
LINK32_FLAGS=kernel32.lib "$(ERLANG_DIR)\usr\lib\erl_dll.lib" "$(EI_DIR)\lib\ei.lib" "$(EI_DIR)\lib\erl_interface.lib" "$(EXPAT_DIR)\libs\libexpat.lib" /nologo /subsystem:console /dll /pdb:none /machine:I386 /out:expat_erl.dll
|
||||
LINK32_OBJS=expat_erl.obj
|
||||
CC=cl.exe
|
||||
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT $(EXPAT_FLAG) -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include" -I"$(EXPAT_DIR)\source\lib"
|
||||
|
||||
expat_erl.dll : $(LINK32_OBJS)
|
||||
$(LINK32) @<<
|
||||
$(LINK32_FLAGS) $(LINK32_OBJS)
|
||||
<<
|
||||
LD=link.exe
|
||||
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" "$(EXPAT_LIB)" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib
|
||||
|
||||
SOURCE=expat_erl.c
|
||||
$(DLL) : $(OBJECT)
|
||||
$(LD) $(LD_FLAGS) -out:$(DLL) $(OBJECT)
|
||||
|
||||
expat_erl.obj : $(SOURCE)
|
||||
$(OBJECT) : $(SOURCE)
|
||||
$(CC) $(CC_FLAGS) -c -Fo$(OBJECT) $(SOURCE)
|
||||
|
||||
|
||||
Vendored
+38
-8
@@ -1,15 +1,14 @@
|
||||
AC_DEFUN(AM_WITH_EXPAT,
|
||||
[ AC_ARG_WITH(expat,
|
||||
[ --with-expat=PREFIX path to expat library],
|
||||
, with_expat=no)
|
||||
[ --with-expat=PREFIX prefix where EXPAT is installed])
|
||||
|
||||
EXPAT_CFLAGS=
|
||||
EXPAT_LIBS=
|
||||
if test $with_expat != no; then
|
||||
if test $with_expat != yes; then
|
||||
if test x"$with_expat" != x; 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 ],
|
||||
@@ -25,7 +24,6 @@ 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)
|
||||
@@ -75,7 +73,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"
|
||||
ERLANG_LIBS="-L$ERLANG_EI_DIR/lib -lerl_interface -lei"
|
||||
|
||||
AC_SUBST(ERLANG_CFLAGS)
|
||||
AC_SUBST(ERLANG_LIBS)
|
||||
@@ -110,9 +108,8 @@ 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=DIR Search for libiconv in DIR/include and DIR/lib], [
|
||||
[ --with-libiconv-prefix=PREFIX prefix where libiconv is installed], [
|
||||
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
|
||||
@@ -193,3 +190,36 @@ 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
|
||||
AC_CHECK_HEADERS($ssl_prefix/include/openssl/ssl.h, have_openssl_h=yes)
|
||||
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"
|
||||
SSL_CFLAGS="-I$ssl_prefix/include/openssl -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/>
|
||||
|
||||
Vendored
+269
-14
@@ -829,13 +829,16 @@ Optional Features:
|
||||
--enable-mod_irc enable mod_irc (default: yes)
|
||||
--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)
|
||||
|
||||
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=DIR Search for libiconv in DIR/include and DIR/lib
|
||||
--with-expat=PREFIX path to expat library
|
||||
--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
|
||||
|
||||
Some influential environment variables:
|
||||
CC C compiler command
|
||||
@@ -2223,7 +2226,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"
|
||||
ERLANG_LIBS="-L$ERLANG_EI_DIR/lib -lerl_interface -lei"
|
||||
|
||||
|
||||
|
||||
@@ -2234,7 +2237,6 @@ 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"
|
||||
@@ -2881,17 +2883,15 @@ done
|
||||
if test "${with_expat+set}" = set; then
|
||||
withval="$with_expat"
|
||||
|
||||
else
|
||||
with_expat=no
|
||||
fi;
|
||||
|
||||
EXPAT_CFLAGS=
|
||||
EXPAT_LIBS=
|
||||
if test $with_expat != no; then
|
||||
if test $with_expat != yes; then
|
||||
if test x"$with_expat" != x; 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
|
||||
@@ -3083,10 +3083,6 @@ echo "$as_me: error: Could not find expat.h" >&2;}
|
||||
{ (exit 1); exit 1; }; }
|
||||
fi
|
||||
CFLAGS="$expat_save_CFLAGS"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3609,7 +3605,6 @@ fi
|
||||
|
||||
|
||||
|
||||
|
||||
mod_pubsub=
|
||||
make_mod_pubsub=
|
||||
echo "$as_me:$LINENO: checking whether build mod_pubsub" >&5
|
||||
@@ -3698,8 +3693,260 @@ echo "${ECHO_T}$mr_enable_eldap" >&6
|
||||
|
||||
|
||||
|
||||
web=
|
||||
make_web=
|
||||
echo "$as_me:$LINENO: checking whether build web" >&5
|
||||
echo $ECHO_N "checking whether build web... $ECHO_C" >&6
|
||||
# Check whether --enable-web or --disable-web was given.
|
||||
if test "${enable_web+set}" = set; then
|
||||
enableval="$enable_web"
|
||||
mr_enable_web="$enableval"
|
||||
else
|
||||
mr_enable_web=yes
|
||||
fi;
|
||||
if test "$mr_enable_web" = yes; then
|
||||
web=web
|
||||
make_web=web/Makefile
|
||||
fi
|
||||
echo "$as_me:$LINENO: result: $mr_enable_web" >&5
|
||||
echo "${ECHO_T}$mr_enable_web" >&6
|
||||
|
||||
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub $make_eldap stringprep/Makefile"
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub $make_eldap $make_web stringprep/Makefile $make_tls"
|
||||
|
||||
#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
|
||||
|
||||
for ac_header in $ssl_prefix/include/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
|
||||
|
||||
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"
|
||||
SSL_CFLAGS="-I$ssl_prefix/include/openssl -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
|
||||
|
||||
cat >confcache <<\_ACEOF
|
||||
# This file is a shell script that caches the results of configure
|
||||
@@ -4202,7 +4449,9 @@ do
|
||||
"$make_mod_muc" ) CONFIG_FILES="$CONFIG_FILES $make_mod_muc" ;;
|
||||
"$make_mod_pubsub" ) CONFIG_FILES="$CONFIG_FILES $make_mod_pubsub" ;;
|
||||
"$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" ;;
|
||||
*) { { 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; }; };;
|
||||
@@ -4309,6 +4558,12 @@ s,@mod_muc@,$mod_muc,;t t
|
||||
s,@make_mod_muc@,$make_mod_muc,;t t
|
||||
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,@SSL_LIBS@,$SSL_LIBS,;t t
|
||||
s,@SSL_CFLAGS@,$SSL_CFLAGS,;t t
|
||||
CEOF
|
||||
|
||||
_ACEOF
|
||||
|
||||
+7
-6
@@ -15,9 +15,6 @@ AM_ICONV
|
||||
#locating libexpat
|
||||
AM_WITH_EXPAT
|
||||
|
||||
|
||||
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
|
||||
@@ -25,17 +22,21 @@ 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_CONFIG_FILES([Makefile
|
||||
$make_mod_irc
|
||||
$make_mod_muc
|
||||
$make_mod_pubsub
|
||||
$make_eldap
|
||||
stringprep/Makefile])
|
||||
$make_web
|
||||
stringprep/Makefile
|
||||
$make_tls])
|
||||
#openssl
|
||||
AM_WITH_OPENSSL
|
||||
AC_OUTPUT
|
||||
|
||||
+17
-1
@@ -1,4 +1,20 @@
|
||||
|
||||
@if "x%1"=="x--help" goto usage
|
||||
|
||||
@set arg=dynamic
|
||||
@if "x%1"=="x--static" set arg=static
|
||||
|
||||
@echo Configuring for %arg% build...
|
||||
|
||||
erlc configure.erl
|
||||
erl -s configure -noshell
|
||||
erl -s configure -env arg %arg% -noshell
|
||||
|
||||
@goto end
|
||||
|
||||
:usage
|
||||
@echo Usage: configure.bat
|
||||
@echo or configure.bat --static
|
||||
@echo or configure.bat --help
|
||||
|
||||
:end
|
||||
|
||||
|
||||
+43
-2
@@ -12,10 +12,51 @@
|
||||
|
||||
-export([start/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
start() ->
|
||||
EIDirS = "EI_DIR = " ++ code:lib_dir("erl_interface") ++ "\n",
|
||||
Static = case os:getenv("arg") of
|
||||
false ->
|
||||
false;
|
||||
"static" ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end,
|
||||
case Static of
|
||||
true ->
|
||||
ExpatLib = "EXPAT_LIB = $(EXPAT_DIR)\\StaticLibs\\libexpatMT.lib\n",
|
||||
ExpatFlag = "EXPAT_FLAG = -DXML_STATIC\n",
|
||||
IconvDir = "ICONV_DIR = c:\\progra~1\\libiconv-1.9.1-static\n",
|
||||
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\iconv.lib\n";
|
||||
false ->
|
||||
ExpatLib = "EXPAT_LIB = $(EXPAT_DIR)\\Libs\\libexpat.lib\n",
|
||||
ExpatFlag = "",
|
||||
IconvDir = "ICONV_DIR = c:\\progra~1\\libiconv-1.9.1\n",
|
||||
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\iconv.lib\n"
|
||||
end,
|
||||
|
||||
EIDirS = "EI_DIR = " ++ code:lib_dir("erl_interface") ++ "\n",
|
||||
RootDirS = "ERLANG_DIR = " ++ code:root_dir() ++ "\n",
|
||||
file:write_file("Makefile.inc", list_to_binary(EIDirS ++ RootDirS)),
|
||||
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",
|
||||
|
||||
file:write_file("Makefile.inc",
|
||||
list_to_binary(EIDirS ++
|
||||
RootDirS ++
|
||||
Version ++
|
||||
SSLDir ++
|
||||
StdLibDir ++
|
||||
OpenSSLDir ++
|
||||
ExpatDir ++
|
||||
ExpatLib ++
|
||||
ExpatFlag ++
|
||||
IconvDir ++
|
||||
IconvLib)),
|
||||
halt().
|
||||
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
AuthzId = xml:get_attr_s("authzid", KeyVals),
|
||||
case ejabberd_auth:get_password(UserName) of
|
||||
false ->
|
||||
{error, "no-user"};
|
||||
{error, "not-authorized"};
|
||||
Passwd ->
|
||||
Response = response(KeyVals, UserName, Passwd,
|
||||
Nonce, AuthzId, "AUTHENTICATE"),
|
||||
@@ -66,7 +66,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
username = UserName,
|
||||
authzid = AuthzId}};
|
||||
_ ->
|
||||
{error, "bad-auth"}
|
||||
{error, "not-authorized"}
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
||||
+60
-29
@@ -11,6 +11,9 @@
|
||||
% Blocked users:
|
||||
%{acl, blocked, {user, "test"}}.
|
||||
|
||||
% Local users:
|
||||
{acl, local, {user_regexp, ""}}.
|
||||
|
||||
% Another examples of ACLs:
|
||||
%{acl, jabberorg, {server, "jabber.org"}}.
|
||||
%{acl, aleksey, {user, "aleksey", "jabber.ru"}}.
|
||||
@@ -61,56 +64,84 @@
|
||||
% Admins of this server are also admins of MUC service:
|
||||
{access, muc_admin, [{allow, admin}]}.
|
||||
|
||||
% All users are allowed to use MUC service:
|
||||
{access, muc, [{allow, all}]}.
|
||||
|
||||
% This rule allows access only for local users:
|
||||
{access, local, [{allow, local}]}.
|
||||
|
||||
|
||||
% Authentification method. If you want to use internal user base, then use
|
||||
% this line:
|
||||
{auth_method, internal}.
|
||||
|
||||
% For LDAP uthentification use these lines instead of above one:
|
||||
% For LDAP authentification 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"}. % Base of LDAP directory
|
||||
|
||||
% For authentification via external script use the following:
|
||||
%{auth_method, external}.
|
||||
%{extauth_program, "/path/to/authentification/script"}.
|
||||
|
||||
|
||||
% Host name:
|
||||
{host, "localhost"}.
|
||||
|
||||
|
||||
% Default language for server messages
|
||||
{language, "en"}.
|
||||
|
||||
% Listened ports:
|
||||
{listen, [{5222, ejabberd_c2s, [{access, c2s},
|
||||
{shaper, c2s_shaper}]},
|
||||
{5223, ejabberd_c2s, [{access, c2s},
|
||||
{ssl, [{certfile, "./ssl.pem"}]}]},
|
||||
{5269, ejabberd_s2s_in, [{shaper, s2s_shaper}]},
|
||||
{8888, ejabberd_service, [{hosts,
|
||||
["conference.e.localhost",
|
||||
"muc.e.localhost"],
|
||||
[{password, "secret"}]}]}
|
||||
]}.
|
||||
{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"}]},
|
||||
{5269, ejabberd_s2s_in, [{shaper, s2s_shaper}]},
|
||||
{5280, ejabberd_http, [http_poll, web_admin]},
|
||||
{8888, ejabberd_service, [{access, all},
|
||||
{hosts, ["icq.localhost", "sms.localhost"],
|
||||
[{password, "secret"}]}]}
|
||||
]}.
|
||||
|
||||
% If SRV lookup fails, then port 5269 is used to communicate with remote server
|
||||
{outgoing_s2s_port, 5269}.
|
||||
|
||||
|
||||
% Used modules:
|
||||
{modules, [
|
||||
{mod_register, []},
|
||||
{mod_roster, []},
|
||||
{mod_privacy, []},
|
||||
{mod_configure, []},
|
||||
{mod_disco, []},
|
||||
{mod_stats, []},
|
||||
{mod_vcard, []},
|
||||
{mod_offline, []},
|
||||
{mod_echo, [{host, "echo.localhost"}]},
|
||||
{mod_private, []},
|
||||
{mod_irc, []},
|
||||
{mod_muc, []},
|
||||
{mod_pubsub, []},
|
||||
{mod_time, []},
|
||||
{mod_last, []},
|
||||
{mod_version, []}
|
||||
]}.
|
||||
{modules,
|
||||
[
|
||||
{mod_register, [{access, register}]},
|
||||
{mod_roster, []},
|
||||
{mod_privacy, []},
|
||||
{mod_configure, []},
|
||||
{mod_configure2, []},
|
||||
{mod_disco, []},
|
||||
{mod_stats, []},
|
||||
{mod_vcard, []},
|
||||
{mod_offline, []},
|
||||
{mod_announce, [{access, announce}]},
|
||||
{mod_echo, [{host, "echo.localhost"}]},
|
||||
{mod_private, []},
|
||||
{mod_irc, []},
|
||||
% Default options for mod_muc:
|
||||
% host: "conference." ++ ?MYNAME
|
||||
% access: all
|
||||
% access_create: all
|
||||
% access_admin: none (only room creator has owner privileges)
|
||||
{mod_muc, [{access, muc},
|
||||
{access_create, muc},
|
||||
{access_admin, muc_admin}]},
|
||||
{mod_pubsub, []},
|
||||
{mod_time, []},
|
||||
{mod_last, []},
|
||||
{mod_version, []}
|
||||
]}.
|
||||
|
||||
|
||||
|
||||
|
||||
+14
-1
@@ -10,7 +10,8 @@
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0, stop/0]).
|
||||
-export([start/0, stop/0,
|
||||
get_so_path/0]).
|
||||
|
||||
start() ->
|
||||
application:start(ejabberd).
|
||||
@@ -19,3 +20,15 @@ stop() ->
|
||||
application:stop(ejabberd).
|
||||
|
||||
|
||||
get_so_path() ->
|
||||
case os:getenv("EJABBERD_SO_PATH") of
|
||||
false ->
|
||||
case code:priv_dir(ejabberd) of
|
||||
{error, _} ->
|
||||
".";
|
||||
Path ->
|
||||
filename:join([Path, "lib"])
|
||||
end;
|
||||
Path ->
|
||||
Path
|
||||
end.
|
||||
|
||||
+2
-1
@@ -6,7 +6,7 @@
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(VERSION, "0.6-alpha").
|
||||
-define(VERSION, "0.8-alpha").
|
||||
|
||||
%-define(ejabberd_debug, true).
|
||||
%-define(DBGFSM, true).
|
||||
@@ -31,6 +31,7 @@
|
||||
-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").
|
||||
-define(CONFIG_PATH, "ejabberd.cfg").
|
||||
|
||||
+14
-2
@@ -51,9 +51,21 @@ init() ->
|
||||
register(ejabberd, self()),
|
||||
%erlang:system_flag(fullsweep_after, 0),
|
||||
%error_logger:logfile({open, ?LOG_PATH}),
|
||||
error_logger:add_report_handler(ejabberd_logger_h, ?LOG_PATH),
|
||||
LogPath =
|
||||
case application:get_env(log_path) of
|
||||
{ok, Path} ->
|
||||
Path;
|
||||
undefined ->
|
||||
case os:getenv("EJABBERD_LOG_PATH") of
|
||||
false ->
|
||||
?LOG_PATH;
|
||||
Path ->
|
||||
Path
|
||||
end
|
||||
end,
|
||||
error_logger:add_report_handler(ejabberd_logger_h, LogPath),
|
||||
%timer:apply_interval(3600000, ?MODULE, dump_ports, []),
|
||||
ok = erl_ddll:load_driver(".", expat_erl),
|
||||
ok = erl_ddll:load_driver(ejabberd:get_so_path(), expat_erl),
|
||||
Port = open_port({spawn, expat_erl}, [binary]),
|
||||
loop(Port).
|
||||
|
||||
|
||||
+59
-9
@@ -47,7 +47,14 @@
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start() ->
|
||||
case auth_method() of
|
||||
external ->
|
||||
extauth:start(ejabberd_config:get_local_option(extauth_program));
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
gen_server:start({local, ejabberd_auth}, ejabberd_auth, [], []).
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ejabberd_auth}, ejabberd_auth, [], []).
|
||||
|
||||
@@ -68,9 +75,12 @@ init([]) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
ok;
|
||||
external ->
|
||||
ok;
|
||||
ldap ->
|
||||
LDAPServers = ejabberd_config:get_local_option(ldap_servers),
|
||||
eldap:start_link("ejabberd", LDAPServers, 389, "", "")
|
||||
eldap:start_link("ejabberd", LDAPServers, 389, "", ""),
|
||||
eldap:start_link("ejabberd_bind", LDAPServers, 389, "", "")
|
||||
end,
|
||||
{ok, #state{}}.
|
||||
|
||||
@@ -123,6 +133,16 @@ terminate(_Reason, _State) ->
|
||||
|
||||
auth_method() ->
|
||||
case ejabberd_config:get_local_option(auth_method) of
|
||||
external ->
|
||||
external;
|
||||
ldap ->
|
||||
ldap;
|
||||
_ ->
|
||||
internal
|
||||
end.
|
||||
|
||||
user_method() ->
|
||||
case ejabberd_config:get_local_option(user_method) of
|
||||
ldap ->
|
||||
ldap;
|
||||
_ ->
|
||||
@@ -133,19 +153,31 @@ plain_password_required() ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
false;
|
||||
external ->
|
||||
true;
|
||||
ldap ->
|
||||
true
|
||||
end.
|
||||
|
||||
|
||||
check_password(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
check_password_internal(User, Password);
|
||||
external ->
|
||||
check_password_external(User, Password);
|
||||
ldap ->
|
||||
check_password_ldap(User, Password)
|
||||
end.
|
||||
|
||||
check_password_external(User, Password) ->
|
||||
extauth:check_password(User, Password).
|
||||
|
||||
set_password_external(User, Password) ->
|
||||
extauth:set_password(User, Password).
|
||||
|
||||
is_user_exists_external(User) ->
|
||||
extauth:is_user_exists(User).
|
||||
|
||||
check_password_internal(User, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
@@ -159,6 +191,8 @@ check_password(User, Password, StreamID, Digest) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
check_password_internal(User, Password, StreamID, Digest);
|
||||
external ->
|
||||
check_password_external(User, Password, StreamID, Digest);
|
||||
ldap ->
|
||||
check_password_ldap(User, Password, StreamID, Digest)
|
||||
end.
|
||||
@@ -176,14 +210,22 @@ check_password_internal(User, Password, StreamID, Digest) ->
|
||||
if DigRes ->
|
||||
true;
|
||||
true ->
|
||||
Passwd == Password
|
||||
(Passwd == Password) and (Password /= "")
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
set_password(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
set_password_internal(User,Password);
|
||||
external ->
|
||||
set_password_external(User,Password);
|
||||
ldap -> {error, not_allowed}
|
||||
end.
|
||||
|
||||
set_password_internal(User, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error -> {error, invalid_jid};
|
||||
LUser ->
|
||||
@@ -199,6 +241,8 @@ try_register(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
try_register_internal(User, Password);
|
||||
external ->
|
||||
{error, not_allowed};
|
||||
ldap ->
|
||||
{error, not_allowed}
|
||||
end.
|
||||
@@ -245,6 +289,8 @@ is_user_exists(User) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
is_user_exists_internal(User);
|
||||
external ->
|
||||
is_user_exists_external(User);
|
||||
ldap ->
|
||||
is_user_exists_ldap(User)
|
||||
end.
|
||||
@@ -261,13 +307,13 @@ is_user_exists_internal(User) ->
|
||||
end.
|
||||
|
||||
remove_user(User) ->
|
||||
case auth_method() of
|
||||
case user_method() of
|
||||
internal ->
|
||||
remove_user_internal(User);
|
||||
ldap ->
|
||||
{error, not_allowed}
|
||||
end.
|
||||
|
||||
|
||||
remove_user_internal(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
@@ -281,7 +327,7 @@ remove_user_internal(User) ->
|
||||
catch mod_private:remove_user(User).
|
||||
|
||||
remove_user(User, Password) ->
|
||||
case auth_method() of
|
||||
case user_method() of
|
||||
internal ->
|
||||
remove_user_internal(User, Password);
|
||||
ldap ->
|
||||
@@ -321,12 +367,15 @@ remove_user_internal(User, Password) ->
|
||||
check_password_ldap(User, Password, StreamID, Digest) ->
|
||||
check_password_ldap(User, Password).
|
||||
|
||||
check_password_external(User, Password, StreamID, Digest) ->
|
||||
check_password_external(User, Password).
|
||||
|
||||
check_password_ldap(User, Password) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
DN ->
|
||||
case eldap:bind("ejabberd", DN, Password) of
|
||||
case eldap:bind("ejabberd_bind", DN, Password) of
|
||||
ok ->
|
||||
true;
|
||||
_ ->
|
||||
@@ -343,7 +392,8 @@ is_user_exists_ldap(User) ->
|
||||
end.
|
||||
|
||||
find_user_dn(User) ->
|
||||
Filter = eldap:equalityMatch("uid", 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},
|
||||
|
||||
+357
-156
@@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_c2s.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Purpose : Serve C2S connection
|
||||
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -16,13 +16,14 @@
|
||||
-export([start/2,
|
||||
start_link/2,
|
||||
send_text/2,
|
||||
send_element/2]).
|
||||
send_element/2,
|
||||
get_presence/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
wait_for_stream/2,
|
||||
wait_for_auth/2,
|
||||
wait_for_sasl_auth/2,
|
||||
wait_for_feature_request/2,
|
||||
wait_for_bind/2,
|
||||
wait_for_session/2,
|
||||
wait_for_sasl_response/2,
|
||||
@@ -44,6 +45,9 @@
|
||||
sasl_state,
|
||||
access,
|
||||
shaper,
|
||||
tls = false,
|
||||
tls_enabled = false,
|
||||
tls_options = [],
|
||||
authentificated = false,
|
||||
jid,
|
||||
user = "", server = ?MYNAME, resource = "",
|
||||
@@ -54,7 +58,8 @@
|
||||
pres_last, pres_pri,
|
||||
pres_timestamp,
|
||||
pres_invis = false,
|
||||
privacy_list = none}).
|
||||
privacy_list = none,
|
||||
lang}).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
@@ -68,15 +73,13 @@
|
||||
"<?xml version='1.0'?>"
|
||||
"<stream:stream xmlns='jabber:client' "
|
||||
"xmlns:stream='http://etherx.jabber.org/streams' "
|
||||
"id='~s' from='~s'~s>"
|
||||
"id='~s' from='~s'~s~s>"
|
||||
).
|
||||
|
||||
-define(STREAM_TRAILER, "</stream:stream>").
|
||||
|
||||
-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)).
|
||||
|
||||
@@ -89,6 +92,10 @@ start(SockData, Opts) ->
|
||||
start_link(SockData, Opts) ->
|
||||
gen_fsm:start_link(ejabberd_c2s, [SockData, Opts], ?FSMOPTS).
|
||||
|
||||
%% Return Username, Resource and presence information
|
||||
get_presence(FsmRef) ->
|
||||
gen_fsm:sync_send_all_state_event(FsmRef, {get_presence}, 1000).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -101,7 +108,6 @@ start_link(SockData, Opts) ->
|
||||
%% {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
|
||||
@@ -110,12 +116,32 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
{value, {_, S}} -> S;
|
||||
_ -> none
|
||||
end,
|
||||
{ok, wait_for_stream, #state{socket = Socket,
|
||||
sockmod = SockMod,
|
||||
receiver = ReceiverPid,
|
||||
streamid = new_id(),
|
||||
access = Access,
|
||||
shaper = Shaper}}.
|
||||
StartTLS = lists:member(starttls, Opts),
|
||||
TLSEnabled = lists:member(tls, Opts),
|
||||
TLS = StartTLS 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_enabled = TLSEnabled,
|
||||
tls_options = TLSOpts,
|
||||
streamid = new_id(),
|
||||
access = Access,
|
||||
shaper = Shaper}}.
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
@@ -124,15 +150,23 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
DefaultLang = case ?MYLANG of
|
||||
undefined ->
|
||||
" xml:lang='en'";
|
||||
DL ->
|
||||
" xml:lang='" ++ DL ++ "'"
|
||||
end,
|
||||
case xml:get_attr_s("xmlns:stream", Attrs) of
|
||||
?NS_STREAM ->
|
||||
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'"]),
|
||||
" version='1.0'",
|
||||
DefaultLang]),
|
||||
send_text(StateData, Header),
|
||||
case StateData#state.authentificated of
|
||||
false ->
|
||||
@@ -143,13 +177,31 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
{xmlelement, "mechanism", [],
|
||||
[{xmlcdata, S}]}
|
||||
end, cyrsasl:listmech()),
|
||||
TLS = StateData#state.tls,
|
||||
TLSEnabled = StateData#state.tls_enabled,
|
||||
SockMod = StateData#state.sockmod,
|
||||
TLSFeature =
|
||||
case (TLS == true) andalso
|
||||
(TLSEnabled == false) andalso
|
||||
(SockMod == gen_tcp) of
|
||||
true ->
|
||||
[{xmlelement, "starttls",
|
||||
[{"xmlns", ?NS_TLS}], []}];
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
TLSFeature ++
|
||||
[{xmlelement, "mechanisms",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
Mechs}]}),
|
||||
{next_state, wait_for_sasl_auth,
|
||||
StateData#state{sasl_state = SASLState}};
|
||||
Mechs},
|
||||
{xmlelement, "register",
|
||||
[{"xmlns", ?NS_FEATURE_IQREGISTER}],
|
||||
[]}]}),
|
||||
{next_state, wait_for_feature_request,
|
||||
StateData#state{sasl_state = SASLState,
|
||||
lang = Lang}};
|
||||
_ ->
|
||||
case StateData#state.resource of
|
||||
"" ->
|
||||
@@ -157,26 +209,30 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
[{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_SASL}], []}]}),
|
||||
{next_state, wait_for_bind, StateData};
|
||||
[{"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}
|
||||
{next_state, wait_for_session,
|
||||
StateData#state{lang = Lang}}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
Header = io_lib:format(
|
||||
?STREAM_HEADER,
|
||||
[StateData#state.streamid, ?MYNAME, ""]),
|
||||
[StateData#state.streamid, ?MYNAME, "", DefaultLang]),
|
||||
send_text(StateData, Header),
|
||||
{next_state, wait_for_auth, StateData}
|
||||
{next_state, wait_for_auth, StateData#state{lang = Lang}}
|
||||
end;
|
||||
_ ->
|
||||
Header = io_lib:format(
|
||||
?STREAM_HEADER,
|
||||
[StateData#state.streamid, ?MYNAME, ""]),
|
||||
[StateData#state.streamid, ?MYNAME, "", ""]),
|
||||
send_text(StateData,
|
||||
Header ++ ?INVALID_NS_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
@@ -184,7 +240,7 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
|
||||
wait_for_stream({xmlstreamerror, _}, StateData) ->
|
||||
Header = io_lib:format(?STREAM_HEADER,
|
||||
["none", ?MYNAME, " version='1.0'"]),
|
||||
["none", ?MYNAME, " version='1.0'", ""]),
|
||||
send_text(StateData,
|
||||
Header ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
@@ -195,12 +251,12 @@ wait_for_stream(closed, StateData) ->
|
||||
|
||||
wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
case is_auth_packet(El) of
|
||||
{auth, ID, get, {"", _, _, _}} ->
|
||||
{auth, _ID, get, {"", _, _, _}} ->
|
||||
Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST),
|
||||
send_element(StateData, Err),
|
||||
{next_state, wait_for_auth, StateData};
|
||||
{auth, ID, get, {U, _, _, _}} ->
|
||||
{xmlelement, Name, Attrs, Els} = jlib:make_result_iq_reply(El),
|
||||
{auth, _ID, get, {U, _, _, _}} ->
|
||||
{xmlelement, Name, Attrs, _Els} = jlib:make_result_iq_reply(El),
|
||||
Res = case ejabberd_auth:plain_password_required() of
|
||||
false ->
|
||||
{xmlelement, Name, Attrs,
|
||||
@@ -214,16 +270,19 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, Name, Attrs,
|
||||
[{xmlelement, "query", [{"xmlns", ?NS_AUTH}],
|
||||
[{xmlelement, "username", [], [{xmlcdata, U}]},
|
||||
{xmlelement, "password", [], []}
|
||||
{xmlelement, "password", [], []},
|
||||
{xmlelement, "resource", [], []}
|
||||
]}]}
|
||||
end,
|
||||
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),
|
||||
{auth, _ID, set, {U, P, D, ""}} ->
|
||||
Err = jlib:make_error_reply(
|
||||
El,
|
||||
?ERR_AUTH_NO_RESOURCE_PROVIDED(StateData#state.lang)),
|
||||
send_element(StateData, Err),
|
||||
{next_state, wait_for_auth, StateData};
|
||||
{auth, ID, set, {U, P, D, R}} ->
|
||||
{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
|
||||
@@ -237,10 +296,15 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID)]),
|
||||
ejabberd_sm:open_session(U, R),
|
||||
Res = jlib:make_result_iq_reply(El),
|
||||
Res1 = jlib:make_result_iq_reply(El),
|
||||
Res = setelement(4, Res1, []),
|
||||
send_element(StateData, Res),
|
||||
change_shaper(StateData, JID),
|
||||
{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) of
|
||||
{'EXIT', _} -> none;
|
||||
@@ -250,8 +314,8 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
StateData#state{user = U,
|
||||
resource = R,
|
||||
jid = JID,
|
||||
pres_f = ?SETS:from_list(Fs),
|
||||
pres_t = ?SETS:from_list(Ts),
|
||||
pres_f = ?SETS:from_list(Fs1),
|
||||
pres_t = ?SETS:from_list(Ts1),
|
||||
privacy_list = PrivList}};
|
||||
_ ->
|
||||
?INFO_MSG(
|
||||
@@ -259,7 +323,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID)]),
|
||||
Err = jlib:make_error_reply(
|
||||
El, ?ERR_FORBIDDEN),
|
||||
El, ?ERR_NOT_AUTHORIZED),
|
||||
send_element(StateData, Err),
|
||||
{next_state, wait_for_auth, StateData}
|
||||
end;
|
||||
@@ -285,10 +349,9 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
end;
|
||||
_ ->
|
||||
case jlib:iq_query_info(El) of
|
||||
{iq, ID, Type, ?NS_REGISTER, SubEl} ->
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""}, {"", ?MYNAME, ""},
|
||||
{iq, ID, Type, ?NS_REGISTER, SubEl}),
|
||||
{"", "", ""}, {"", ?MYNAME, ""}, IQ),
|
||||
Res1 = jlib:replace_from_to({"", ?MYNAME, ""},
|
||||
{"", "", ""},
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
@@ -300,7 +363,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
end
|
||||
end;
|
||||
|
||||
wait_for_auth({xmlstreamend, Name}, StateData) ->
|
||||
wait_for_auth({xmlstreamend, _Name}, StateData) ->
|
||||
send_text(StateData, ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
@@ -312,8 +375,11 @@ wait_for_auth(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
wait_for_sasl_auth({xmlstreamelement, El}, StateData) ->
|
||||
wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, Name, Attrs, Els} = El,
|
||||
TLS = StateData#state.tls,
|
||||
TLSEnabled = StateData#state.tls_enabled,
|
||||
SockMod = StateData#state.sockmod,
|
||||
case {xml:get_attr_s("xmlns", Attrs), Name} of
|
||||
{?NS_SASL, "auth"} ->
|
||||
Mech = xml:get_attr_s("mechanism", Attrs),
|
||||
@@ -346,34 +412,47 @@ wait_for_sasl_auth({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, "failure",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
[{xmlelement, Error, [], []}]}),
|
||||
{next_state, wait_for_sasl_auth, StateData}
|
||||
{next_state, wait_for_feature_request, 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,
|
||||
tls_enabled = true
|
||||
}};
|
||||
_ ->
|
||||
case jlib:iq_query_info(El) of
|
||||
{iq, ID, Type, ?NS_REGISTER, SubEl} ->
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""}, {"", ?MYNAME, ""},
|
||||
{iq, ID, Type, ?NS_REGISTER, SubEl}),
|
||||
{"", "", ""}, {"", ?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_feature_request, StateData};
|
||||
_ ->
|
||||
{next_state, wait_for_sasl_auth, StateData}
|
||||
{next_state, wait_for_feature_request, StateData}
|
||||
end
|
||||
end;
|
||||
|
||||
wait_for_sasl_auth({xmlstreamend, Name}, StateData) ->
|
||||
wait_for_feature_request({xmlstreamend, _Name}, StateData) ->
|
||||
send_text(StateData, ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_sasl_auth({xmlstreamerror, _}, StateData) ->
|
||||
wait_for_feature_request({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_sasl_auth(closed, StateData) ->
|
||||
wait_for_feature_request(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
@@ -409,26 +488,25 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, "failure",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
[{xmlelement, Error, [], []}]}),
|
||||
{next_state, wait_for_sasl_auth, StateData}
|
||||
{next_state, wait_for_feature_request, StateData}
|
||||
end;
|
||||
_ ->
|
||||
case jlib:iq_query_info(El) of
|
||||
{iq, ID, Type, ?NS_REGISTER, SubEl} ->
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""}, {"", ?MYNAME, ""},
|
||||
{iq, ID, Type, ?NS_REGISTER, SubEl}),
|
||||
{"", "", ""}, {"", ?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_feature_request, StateData};
|
||||
_ ->
|
||||
{next_state, wait_for_sasl_auth, StateData}
|
||||
{next_state, wait_for_feature_request, StateData}
|
||||
end
|
||||
end;
|
||||
|
||||
wait_for_sasl_response({xmlstreamend, Name}, StateData) ->
|
||||
wait_for_sasl_response({xmlstreamend, _Name}, StateData) ->
|
||||
send_text(StateData, ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
@@ -443,7 +521,7 @@ wait_for_sasl_response(closed, StateData) ->
|
||||
|
||||
wait_for_bind({xmlstreamelement, El}, StateData) ->
|
||||
case jlib:iq_query_info(El) of
|
||||
{iq, ID, set, ?NS_BIND, SubEl} ->
|
||||
#iq{type = set, xmlns = ?NS_BIND, sub_el = SubEl} = IQ ->
|
||||
U = StateData#state.user,
|
||||
R1 = xml:get_path_s(SubEl, [{elem, "resource"}, cdata]),
|
||||
R = case jlib:resourceprep(R1) of
|
||||
@@ -460,11 +538,12 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
|
||||
{next_state, wait_for_bind, StateData};
|
||||
_ ->
|
||||
JID = jlib:make_jid(U, StateData#state.server, R),
|
||||
Res = {iq, ID, result, ?NS_BIND,
|
||||
[{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}],
|
||||
[{xmlelement, "jid", [],
|
||||
[{xmlcdata, jlib:jid_to_string(JID)}]}]}]},
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}],
|
||||
[{xmlelement, "jid", [],
|
||||
[{xmlcdata,
|
||||
jlib:jid_to_string(JID)}]}]}]},
|
||||
send_element(StateData, jlib:iq_to_xml(Res)),
|
||||
{next_state, wait_for_session,
|
||||
StateData#state{resource = R, jid = JID}}
|
||||
@@ -473,7 +552,7 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
|
||||
{next_state, wait_for_bind, StateData}
|
||||
end;
|
||||
|
||||
wait_for_bind({xmlstreamend, Name}, StateData) ->
|
||||
wait_for_bind({xmlstreamend, _Name}, StateData) ->
|
||||
send_text(StateData, ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
@@ -488,11 +567,11 @@ wait_for_bind(closed, StateData) ->
|
||||
|
||||
wait_for_session({xmlstreamelement, El}, StateData) ->
|
||||
case jlib:iq_query_info(El) of
|
||||
{iq, ID, set, ?NS_SESSION, SubEl} ->
|
||||
#iq{type = set, xmlns = ?NS_SESSION} ->
|
||||
U = StateData#state.user,
|
||||
R = StateData#state.resource,
|
||||
io:format("SASLAUTH: ~p~n", [{U, R}]),
|
||||
JID = jlib:make_jid(U, StateData#state.server, R),
|
||||
JID = StateData#state.jid,
|
||||
case acl:match_rule(StateData#state.access, JID) of
|
||||
allow ->
|
||||
?INFO_MSG("(~w) Opened session for ~s",
|
||||
@@ -503,14 +582,17 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
|
||||
send_element(StateData, Res),
|
||||
change_shaper(StateData, JID),
|
||||
{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) of
|
||||
{'EXIT', _} -> none;
|
||||
PL -> PL
|
||||
end,
|
||||
{next_state, session_established,
|
||||
StateData#state{pres_f = ?SETS:from_list(Fs),
|
||||
pres_t = ?SETS:from_list(Ts),
|
||||
StateData#state{pres_f = ?SETS:from_list(Fs1),
|
||||
pres_t = ?SETS:from_list(Ts1),
|
||||
privacy_list = PrivList}};
|
||||
_ ->
|
||||
?INFO_MSG("(~w) Forbidden session for ~s",
|
||||
@@ -520,22 +602,11 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
|
||||
send_element(StateData, Err),
|
||||
{next_state, wait_for_session, StateData}
|
||||
end;
|
||||
% TODO: is this needed?
|
||||
{iq, ID, Type, ?NS_REGISTER, SubEl} ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""}, {"", ?MYNAME, ""},
|
||||
{iq, ID, Type, ?NS_REGISTER, SubEl}),
|
||||
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_session, StateData};
|
||||
_ ->
|
||||
{next_state, wait_for_session, StateData}
|
||||
end;
|
||||
|
||||
wait_for_session({xmlstreamend, Name}, StateData) ->
|
||||
wait_for_session({xmlstreamend, _Name}, StateData) ->
|
||||
send_text(StateData, ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
@@ -550,12 +621,10 @@ wait_for_session(closed, StateData) ->
|
||||
|
||||
|
||||
session_established({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, Name, Attrs, Els} = El,
|
||||
{xmlelement, Name, Attrs, _Els} = El,
|
||||
User = StateData#state.user,
|
||||
Server = StateData#state.server,
|
||||
%FromJID = {User,
|
||||
% Server,
|
||||
% StateData#state.resource},
|
||||
% TODO: check 'from' attribute in stanza
|
||||
FromJID = StateData#state.jid,
|
||||
To = xml:get_attr_s("to", Attrs),
|
||||
ToJID = case To of
|
||||
@@ -564,6 +633,16 @@ session_established({xmlstreamelement, El}, StateData) ->
|
||||
_ ->
|
||||
jlib:string_to_jid(To)
|
||||
end,
|
||||
NewEl = case xml:get_attr_s("xml:lang", Attrs) of
|
||||
"" ->
|
||||
case StateData#state.lang of
|
||||
"" -> El;
|
||||
Lang ->
|
||||
xml:replace_tag_attr("xml:lang", Lang, El)
|
||||
end;
|
||||
_ ->
|
||||
El
|
||||
end,
|
||||
NewState =
|
||||
case ToJID of
|
||||
error ->
|
||||
@@ -571,7 +650,7 @@ session_established({xmlstreamelement, El}, StateData) ->
|
||||
"error" -> StateData;
|
||||
"result" -> StateData;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(El, ?ERR_JID_MALFORMED),
|
||||
Err = jlib:make_error_reply(NewEl, ?ERR_JID_MALFORMED),
|
||||
send_element(StateData, Err),
|
||||
StateData
|
||||
end;
|
||||
@@ -583,28 +662,34 @@ session_established({xmlstreamelement, El}, StateData) ->
|
||||
server = Server,
|
||||
resource = ""} ->
|
||||
?DEBUG("presence_update(~p,~n\t~p,~n\t~p)",
|
||||
[FromJID, El, StateData]),
|
||||
presence_update(FromJID, El, StateData);
|
||||
[FromJID, NewEl, StateData]),
|
||||
presence_update(FromJID, NewEl, StateData);
|
||||
_ ->
|
||||
presence_track(FromJID, ToJID, El, StateData)
|
||||
presence_track(FromJID, ToJID, NewEl, StateData)
|
||||
end;
|
||||
"iq" ->
|
||||
case StateData#state.privacy_list of
|
||||
none ->
|
||||
ejabberd_router:route(FromJID, ToJID, El),
|
||||
ejabberd_router:route(FromJID, ToJID, NewEl),
|
||||
StateData;
|
||||
PrivList ->
|
||||
case jlib:iq_query_info(El) of
|
||||
{iq, ID, Type, ?NS_PRIVACY = XMLNS, SubEl} = IQ ->
|
||||
_PrivList ->
|
||||
case jlib:iq_query_info(NewEl) of
|
||||
#iq{xmlns = ?NS_PRIVACY} = IQ ->
|
||||
process_privacy_iq(
|
||||
FromJID, ToJID, IQ, StateData);
|
||||
_ ->
|
||||
ejabberd_router:route(FromJID, ToJID, El),
|
||||
ejabberd_hooks:run(
|
||||
user_send_packet,
|
||||
[FromJID, ToJID, NewEl]),
|
||||
ejabberd_router:route(
|
||||
FromJID, ToJID, NewEl),
|
||||
StateData
|
||||
end
|
||||
end;
|
||||
"message" ->
|
||||
ejabberd_router:route(FromJID, ToJID, El),
|
||||
ejabberd_hooks:run(user_send_packet,
|
||||
[FromJID, ToJID, NewEl]),
|
||||
ejabberd_router:route(FromJID, ToJID, NewEl),
|
||||
StateData;
|
||||
_ ->
|
||||
StateData
|
||||
@@ -612,7 +697,7 @@ session_established({xmlstreamelement, El}, StateData) ->
|
||||
end,
|
||||
{next_state, session_established, NewState};
|
||||
|
||||
session_established({xmlstreamend, Name}, StateData) ->
|
||||
session_established({xmlstreamend, _Name}, StateData) ->
|
||||
send_text(StateData, ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
@@ -644,7 +729,7 @@ session_established(closed, StateData) ->
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(Event, StateName, StateData) ->
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
@@ -656,11 +741,23 @@ handle_event(Event, StateName, StateData) ->
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event(Event, From, StateName, StateData) ->
|
||||
handle_sync_event({get_presence}, _From, StateName, StateData) ->
|
||||
User = StateData#state.user,
|
||||
PresLast = StateData#state.pres_last,
|
||||
|
||||
Show = get_showtag(PresLast),
|
||||
Status = get_statustag(PresLast),
|
||||
Resource = StateData#state.resource,
|
||||
|
||||
Reply = {User, Resource, Show, Status},
|
||||
{reply, Reply, StateName, StateData};
|
||||
|
||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData}.
|
||||
|
||||
code_change(OldVsn, StateName, StateData, Extra) ->
|
||||
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
@@ -672,10 +769,10 @@ code_change(OldVsn, StateName, StateData, Extra) ->
|
||||
handle_info({send_text, Text}, StateName, StateData) ->
|
||||
send_text(StateData, Text),
|
||||
{next_state, StateName, StateData};
|
||||
handle_info(replaced, StateName, StateData) ->
|
||||
handle_info(replaced, _StateName, StateData) ->
|
||||
% TODO
|
||||
%send_text(StateData#state.sender, Text),
|
||||
{stop, normal, StateData#state{user = ""}};
|
||||
{stop, normal, StateData#state{authentificated = replaced}};
|
||||
handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
{Pass, NewAttrs, NewState} =
|
||||
@@ -720,37 +817,37 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
end;
|
||||
"broadcast" ->
|
||||
?DEBUG("broadcast~n~p~n", [Els]),
|
||||
NewSt = case Els of
|
||||
[{item, IJID, ISubscription}] ->
|
||||
{false, Attrs,
|
||||
roster_change(IJID, ISubscription,
|
||||
StateData)};
|
||||
[{exit, Reason}] ->
|
||||
{exit, Attrs, Reason};
|
||||
[{privacy_list, PrivList}] ->
|
||||
{false, Attrs,
|
||||
case catch mod_privacy:updated_list(
|
||||
StateData#state.privacy_list,
|
||||
PrivList) of
|
||||
{'EXIT', _} ->
|
||||
{false, Attrs, StateData};
|
||||
NewPL ->
|
||||
StateData#state{privacy_list = NewPL}
|
||||
end};
|
||||
_ ->
|
||||
{false, Attrs, StateData}
|
||||
end;
|
||||
case Els of
|
||||
[{item, IJID, ISubscription}] ->
|
||||
{false, Attrs,
|
||||
roster_change(IJID, ISubscription,
|
||||
StateData)};
|
||||
[{exit, Reason}] ->
|
||||
{exit, Attrs, Reason};
|
||||
[{privacy_list, PrivList}] ->
|
||||
{false, Attrs,
|
||||
case catch mod_privacy:updated_list(
|
||||
StateData#state.privacy_list,
|
||||
PrivList) of
|
||||
{'EXIT', _} ->
|
||||
{false, Attrs, StateData};
|
||||
NewPL ->
|
||||
StateData#state{privacy_list = NewPL}
|
||||
end};
|
||||
_ ->
|
||||
{false, Attrs, StateData}
|
||||
end;
|
||||
"iq" ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
{iq, ID, Type, ?NS_VCARD, SubEl} ->
|
||||
#iq{xmlns = ?NS_VCARD} ->
|
||||
ResIQ = mod_vcard:process_sm_iq(From, To, IQ),
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
{false, Attrs, StateData};
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
{iq, _ID, Type, _XMLNS, _SubEl} ->
|
||||
#iq{} ->
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.privacy_list,
|
||||
@@ -795,8 +892,11 @@ 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),
|
||||
Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}),
|
||||
FixedPacket = {xmlelement, Name, Attrs2, Els},
|
||||
Text = xml:element_to_string(FixedPacket),
|
||||
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}
|
||||
@@ -807,20 +907,53 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StateData) ->
|
||||
terminate(_Reason, StateName, StateData) ->
|
||||
case StateName of
|
||||
session_established ->
|
||||
?INFO_MSG("(~w) Close session for ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(StateData#state.jid)]),
|
||||
ejabberd_sm:close_session(StateData#state.user,
|
||||
StateData#state.resource),
|
||||
From = StateData#state.jid,
|
||||
Packet = {xmlelement, "presence", [{"type", "unavailable"}], []},
|
||||
ejabberd_sm:unset_presence(StateData#state.user,
|
||||
StateData#state.resource),
|
||||
presence_broadcast(StateData, From, StateData#state.pres_a, Packet),
|
||||
presence_broadcast(StateData, From, StateData#state.pres_i, Packet);
|
||||
case StateData#state.authentificated of
|
||||
replaced ->
|
||||
?INFO_MSG("(~w) Replaced session for ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(StateData#state.jid)]),
|
||||
From = StateData#state.jid,
|
||||
Packet = {xmlelement, "presence",
|
||||
[{"type", "unavailable"}],
|
||||
[{xmlelement, "status", [],
|
||||
[{xmlcdata, "Replaced by new connection"}]}]},
|
||||
ejabberd_sm:unset_presence(StateData#state.user,
|
||||
StateData#state.resource,
|
||||
"Replaced by new connection"),
|
||||
presence_broadcast(
|
||||
StateData, From, StateData#state.pres_a, Packet),
|
||||
presence_broadcast(
|
||||
StateData, From, StateData#state.pres_i, Packet);
|
||||
_ ->
|
||||
?INFO_MSG("(~w) Close session for ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(StateData#state.jid)]),
|
||||
ejabberd_sm:close_session(StateData#state.user,
|
||||
StateData#state.resource),
|
||||
|
||||
Tmp = ?SETS:new(),
|
||||
case StateData of
|
||||
#state{pres_last = undefined,
|
||||
pres_a = Tmp,
|
||||
pres_i = Tmp,
|
||||
pres_invis = false} ->
|
||||
ok;
|
||||
_ ->
|
||||
From = StateData#state.jid,
|
||||
Packet = {xmlelement, "presence",
|
||||
[{"type", "unavailable"}], []},
|
||||
ejabberd_sm:unset_presence(StateData#state.user,
|
||||
StateData#state.resource,
|
||||
""),
|
||||
presence_broadcast(
|
||||
StateData, From, StateData#state.pres_a, Packet),
|
||||
presence_broadcast(
|
||||
StateData, From, StateData#state.pres_i, Packet)
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
@@ -848,7 +981,7 @@ new_id() ->
|
||||
|
||||
is_auth_packet(El) ->
|
||||
case jlib:iq_query_info(El) of
|
||||
{iq, ID, Type, ?NS_AUTH, SubEl} ->
|
||||
#iq{id = ID, type = Type, xmlns = ?NS_AUTH, sub_el = SubEl} ->
|
||||
{xmlelement, _, _, Els} = SubEl,
|
||||
{auth, ID, Type,
|
||||
get_auth_tags(Els, "", "", "", "")};
|
||||
@@ -857,7 +990,7 @@ is_auth_packet(El) ->
|
||||
end.
|
||||
|
||||
|
||||
get_auth_tags([{xmlelement, Name, Attrs, Els}| L], U, P, D, R) ->
|
||||
get_auth_tags([{xmlelement, Name, _Attrs, Els}| L], U, P, D, R) ->
|
||||
CData = xml:get_cdata(Els),
|
||||
case Name of
|
||||
"username" ->
|
||||
@@ -879,16 +1012,24 @@ get_auth_tags([], U, P, D, R) ->
|
||||
|
||||
process_presence_probe(From, To, StateData) ->
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
LBFrom = setelement(3, LFrom, ""),
|
||||
case StateData#state.pres_last of
|
||||
undefined ->
|
||||
ok;
|
||||
_ ->
|
||||
Cond1 = (not StateData#state.pres_invis)
|
||||
and ?SETS:is_element(LFrom, StateData#state.pres_f)
|
||||
and (not ?SETS:is_element(LFrom, StateData#state.pres_i)),
|
||||
andalso (?SETS:is_element(LFrom, StateData#state.pres_f)
|
||||
orelse
|
||||
((LFrom /= LBFrom) andalso
|
||||
?SETS:is_element(LBFrom, StateData#state.pres_f)))
|
||||
andalso (not
|
||||
(?SETS:is_element(LFrom, StateData#state.pres_i)
|
||||
orelse
|
||||
((LFrom /= LBFrom) andalso
|
||||
?SETS:is_element(LBFrom, StateData#state.pres_i)))),
|
||||
Cond2 = StateData#state.pres_invis
|
||||
and ?SETS:is_element(LFrom, StateData#state.pres_f)
|
||||
and ?SETS:is_element(LFrom, StateData#state.pres_a),
|
||||
andalso ?SETS:is_element(LFrom, StateData#state.pres_f)
|
||||
andalso ?SETS:is_element(LFrom, StateData#state.pres_a),
|
||||
if
|
||||
Cond1 ->
|
||||
Packet = StateData#state.pres_last,
|
||||
@@ -917,11 +1058,18 @@ process_presence_probe(From, To, StateData) ->
|
||||
end.
|
||||
|
||||
presence_update(From, Packet, StateData) ->
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
{xmlelement, _Name, Attrs, _Els} = Packet,
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"unavailable" ->
|
||||
Status = case xml:get_subtag(Packet, "status") of
|
||||
false ->
|
||||
"";
|
||||
StatusTag ->
|
||||
xml:get_tag_cdata(StatusTag)
|
||||
end,
|
||||
ejabberd_sm:unset_presence(StateData#state.user,
|
||||
StateData#state.resource),
|
||||
StateData#state.resource,
|
||||
Status),
|
||||
presence_broadcast(StateData, From, StateData#state.pres_a, Packet),
|
||||
presence_broadcast(StateData, From, StateData#state.pres_i, Packet),
|
||||
StateData#state{pres_last = undefined,
|
||||
@@ -949,6 +1097,8 @@ presence_update(From, Packet, StateData) ->
|
||||
NewState;
|
||||
"error" ->
|
||||
StateData;
|
||||
"probe" ->
|
||||
StateData;
|
||||
"subscribe" ->
|
||||
StateData;
|
||||
"subscribed" ->
|
||||
@@ -965,10 +1115,9 @@ presence_update(From, Packet, StateData) ->
|
||||
NewState =
|
||||
if
|
||||
FromUnavail ->
|
||||
% TODO: watching ourself
|
||||
|
||||
catch mod_offline:resend_offline_messages(
|
||||
StateData#state.user),
|
||||
ejabberd_hooks:run(user_available_hook,
|
||||
[StateData#state.jid]),
|
||||
resend_offline_messages(StateData),
|
||||
presence_broadcast_first(
|
||||
From, StateData#state{pres_last = Packet,
|
||||
pres_invis = false
|
||||
@@ -987,7 +1136,7 @@ presence_update(From, Packet, StateData) ->
|
||||
end.
|
||||
|
||||
presence_track(From, To, Packet, StateData) ->
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
{xmlelement, _Name, Attrs, _Els} = Packet,
|
||||
LTo = jlib:jid_tolower(To),
|
||||
User = StateData#state.user,
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
@@ -1022,6 +1171,9 @@ presence_track(From, To, Packet, StateData) ->
|
||||
"error" ->
|
||||
ejabberd_router:route(From, To, Packet),
|
||||
StateData;
|
||||
"probe" ->
|
||||
ejabberd_router:route(From, To, Packet),
|
||||
StateData;
|
||||
_ ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
@@ -1091,7 +1243,7 @@ presence_broadcast_to_trusted(StateData, From, T, A, Packet) ->
|
||||
presence_broadcast_first(From, StateData, Packet) ->
|
||||
?SETS:fold(fun(JID, X) ->
|
||||
ejabberd_router:route(
|
||||
jlib:jid_replace_resource(From, ""),
|
||||
From,
|
||||
jlib:make_jid(JID),
|
||||
{xmlelement, "presence",
|
||||
[{"type", "probe"}],
|
||||
@@ -1155,7 +1307,7 @@ roster_change(IJID, ISubscription, StateData) ->
|
||||
remove_element(LIJID, StateData#state.pres_t)
|
||||
end,
|
||||
case StateData#state.pres_last of
|
||||
unknown ->
|
||||
undefined ->
|
||||
StateData#state{pres_f = FSet, pres_t = TSet};
|
||||
P ->
|
||||
?DEBUG("roster changed for ~p~n", [StateData#state.user]),
|
||||
@@ -1207,10 +1359,12 @@ update_priority(El, StateData) ->
|
||||
ejabberd_sm:set_presence(StateData#state.user,
|
||||
StateData#state.resource,
|
||||
Pri).
|
||||
|
||||
|
||||
|
||||
process_privacy_iq(From, To, {iq, ID, Type, XMLNS, SubEl} = IQ, StateData) ->
|
||||
|
||||
process_privacy_iq(From, To,
|
||||
#iq{type = Type, sub_el = SubEl} = IQ,
|
||||
StateData) ->
|
||||
{Res, NewStateData} =
|
||||
case Type of
|
||||
get ->
|
||||
@@ -1237,13 +1391,60 @@ process_privacy_iq(From, To, {iq, ID, Type, XMLNS, SubEl} = IQ, StateData) ->
|
||||
IQRes =
|
||||
case Res of
|
||||
{result, Result} ->
|
||||
{iq, ID, result, XMLNS, Result};
|
||||
IQ#iq{type = result, sub_el = Result};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, Error]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
end,
|
||||
ejabberd_router:route(
|
||||
To, From, jlib:iq_to_xml(IQRes)),
|
||||
NewStateData.
|
||||
|
||||
|
||||
resend_offline_messages(#state{user = User,
|
||||
privacy_list = PrivList} = StateData) ->
|
||||
case ejabberd_hooks:run_fold(resend_offline_messages_hook, [],
|
||||
[User]) of
|
||||
Rs when list(Rs) ->
|
||||
lists:foreach(
|
||||
fun({route,
|
||||
From, To, {xmlelement, Name, Attrs, Els} = Packet}) ->
|
||||
Pass = case catch mod_privacy:check_packet(
|
||||
User,
|
||||
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
|
||||
end, Rs)
|
||||
end.
|
||||
|
||||
get_showtag(undefined) ->
|
||||
"unavailable";
|
||||
get_showtag(Presence) ->
|
||||
case xml:get_path_s(Presence, [{elem, "show"}, cdata]) of
|
||||
"" -> "available";
|
||||
ShowTag -> ShowTag
|
||||
end.
|
||||
|
||||
get_statustag(undefined) ->
|
||||
"";
|
||||
get_statustag(Presence) ->
|
||||
case xml:get_path_s(Presence, [{elem, "status"}, cdata]) of
|
||||
ShowTag -> ShowTag
|
||||
end.
|
||||
|
||||
+11
-1
@@ -34,9 +34,17 @@ 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 -> ?CONFIG_PATH
|
||||
undefined ->
|
||||
case os:getenv("EJABBERD_CONFIG_PATH") of
|
||||
false ->
|
||||
?CONFIG_PATH;
|
||||
Path ->
|
||||
Path
|
||||
end
|
||||
end,
|
||||
load_file(Config).
|
||||
|
||||
@@ -78,6 +86,8 @@ add_option(Opt, Val, State) ->
|
||||
Table = case Opt of
|
||||
host ->
|
||||
config;
|
||||
language ->
|
||||
config;
|
||||
_ ->
|
||||
local_config
|
||||
end,
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_ctl.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Ejabberd admin tool
|
||||
%%% Created : 11 Jan 2004 by Alexey Shchepin <alex@alex.sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_ctl).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-export([start/0]).
|
||||
|
||||
-define(STATUS_SUCCESS, 0).
|
||||
-define(STATUS_ERROR, 1).
|
||||
-define(STATUS_USAGE, 2).
|
||||
-define(STATUS_BADRPC, 3).
|
||||
|
||||
start() ->
|
||||
case init:get_plain_arguments() of
|
||||
[SNode | Args] ->
|
||||
Node = list_to_atom(SNode),
|
||||
Status = process(Node, Args),
|
||||
halt(Status);
|
||||
_ ->
|
||||
print_usage(),
|
||||
halt(?STATUS_USAGE)
|
||||
end.
|
||||
|
||||
|
||||
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;
|
||||
_ ->
|
||||
?STATUS_SUCCESS
|
||||
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;
|
||||
_ ->
|
||||
?STATUS_SUCCESS
|
||||
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;
|
||||
|
||||
process(Node, ["register", User, Password]) ->
|
||||
case rpc:call(Node, ejabberd_auth, try_register, [User, Password]) of
|
||||
{atomic, ok} ->
|
||||
?STATUS_SUCCESS;
|
||||
{atomic, exists} ->
|
||||
io:format("User ~p already registered on node ~p~n",
|
||||
[User, Node]),
|
||||
?STATUS_ERROR;
|
||||
{error, Reason} ->
|
||||
io:format("Can't register user ~p on node ~p: ~p~n",
|
||||
[User, Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't register user ~p on node ~p: ~p~n",
|
||||
[User, Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
end;
|
||||
|
||||
process(Node, ["unregister", User]) ->
|
||||
case rpc:call(Node, ejabberd_auth, remove_user, [User]) of
|
||||
{atomic, ok} ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't unregister user ~p on node ~p: ~p~n",
|
||||
[User, Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't unregister user ~p on node ~p: ~p~n",
|
||||
[User, Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
end;
|
||||
|
||||
process(Node, ["backup", Path]) ->
|
||||
case rpc:call(Node, mnesia, backup, [Path]) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't store backup in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't store backup in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
end;
|
||||
|
||||
process(Node, ["dump", Path]) ->
|
||||
case rpc:call(Node, mnesia, dump_to_textfile, [Path]) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't store dump in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't store dump in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
end;
|
||||
|
||||
process(Node, ["load", Path]) ->
|
||||
case rpc:call(Node, mnesia, load_textfile, [Path]) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't load dump in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't load dump in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
end;
|
||||
|
||||
process(Node, ["restore", Path]) ->
|
||||
case rpc:call(Node,
|
||||
mnesia, restore, [Path, [{default_op, keep_tables}]]) of
|
||||
{atomic, ok} ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't restore backup from ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't restore backup from ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
end;
|
||||
|
||||
process(Node, ["install-fallback", Path]) ->
|
||||
case rpc:call(Node, mnesia, install_fallback, [Path]) of
|
||||
{atomic, ok} ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't install fallback from ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't install fallback from ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
end;
|
||||
|
||||
process(Node, ["registered-users"]) ->
|
||||
case rpc:call(Node, ejabberd_auth, dirty_get_registered_users, []) of
|
||||
Users when is_list(Users) ->
|
||||
NewLine = io_lib:format("~n", []),
|
||||
SUsers = lists:sort(Users),
|
||||
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 on node ~p: ~p~n",
|
||||
[Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't get list of registered users on 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
|
||||
end;
|
||||
|
||||
process(_Node, _Args) ->
|
||||
print_usage(),
|
||||
?STATUS_USAGE.
|
||||
|
||||
|
||||
|
||||
print_usage() ->
|
||||
io:format(
|
||||
"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 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 in a text file~n"
|
||||
" load file\t\t\trestore a database from a text file~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"
|
||||
).
|
||||
@@ -0,0 +1,183 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% 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.
|
||||
|
||||
|
||||
|
||||
+102
-17
@@ -12,7 +12,11 @@
|
||||
|
||||
-export([start_link/0, init/1, start/3,
|
||||
init/3,
|
||||
init_ssl/4
|
||||
init_ssl/4,
|
||||
start_listener/3,
|
||||
stop_listener/1,
|
||||
add_listener/3,
|
||||
delete_listener/1
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -31,7 +35,7 @@ init(_) ->
|
||||
fun({Port, Module, Opts}) ->
|
||||
{Port,
|
||||
{?MODULE, start, [Port, Module, Opts]},
|
||||
permanent,
|
||||
transient,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[?MODULE]}
|
||||
@@ -45,16 +49,37 @@ start(Port, Module, Opts) ->
|
||||
{ok, proc_lib:spawn_link(?MODULE, init_ssl,
|
||||
[Port, Module, Opts, SSLOpts])};
|
||||
_ ->
|
||||
{ok, proc_lib:spawn_link(?MODULE, init, [Port, Module, Opts])}
|
||||
case lists:member(ssl, Opts) of
|
||||
true ->
|
||||
{ok, proc_lib:spawn_link(?MODULE, init_ssl,
|
||||
[Port, Module, Opts, []])};
|
||||
false ->
|
||||
{ok, proc_lib:spawn_link(?MODULE, init,
|
||||
[Port, Module, Opts])}
|
||||
end
|
||||
end.
|
||||
|
||||
init(Port, Module, Opts) ->
|
||||
{ok, ListenSocket} = gen_tcp:listen(Port, [binary,
|
||||
{packet, 0},
|
||||
{active, false},
|
||||
{reuseaddr, true},
|
||||
{nodelay, true}]),
|
||||
accept(ListenSocket, Module, Opts).
|
||||
SockOpts = lists:filter(fun({ip, _}) -> true;
|
||||
(inet6) -> true;
|
||||
(inet) -> true;
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
|
||||
Res = gen_tcp:listen(Port, [binary,
|
||||
{packet, 0},
|
||||
{active, false},
|
||||
{reuseaddr, true},
|
||||
{nodelay, true} |
|
||||
SockOpts]),
|
||||
case Res of
|
||||
{ok, ListenSocket} ->
|
||||
accept(ListenSocket, Module, Opts);
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Failed to open socket for ~p: ~p",
|
||||
[{Port, Module, Opts}, Reason]),
|
||||
error
|
||||
end.
|
||||
|
||||
accept(ListenSocket, Module, Opts) ->
|
||||
case gen_tcp:accept(ListenSocket) of
|
||||
@@ -67,7 +92,12 @@ accept(ListenSocket, Module, Opts) ->
|
||||
ok
|
||||
end,
|
||||
{ok, Pid} = Module:start({gen_tcp, Socket}, Opts),
|
||||
gen_tcp:controlling_process(Socket, Pid),
|
||||
case gen_tcp:controlling_process(Socket, Pid) of
|
||||
ok ->
|
||||
ok;
|
||||
{error, _Reason} ->
|
||||
gen_tcp:close(Socket)
|
||||
end,
|
||||
accept(ListenSocket, Module, Opts);
|
||||
{error, Reason} ->
|
||||
?INFO_MSG("(~w) Failed TCP accept: ~w",
|
||||
@@ -77,12 +107,31 @@ accept(ListenSocket, Module, Opts) ->
|
||||
|
||||
|
||||
init_ssl(Port, Module, Opts, SSLOpts) ->
|
||||
{ok, ListenSocket} = ssl:listen(Port, [binary,
|
||||
{packet, 0},
|
||||
{active, false},
|
||||
{nodelay, true} |
|
||||
SSLOpts]),
|
||||
accept_ssl(ListenSocket, Module, Opts).
|
||||
SockOpts = lists:filter(fun({ip, _}) -> true;
|
||||
(inet6) -> true;
|
||||
(inet) -> true;
|
||||
({verify, _}) -> true;
|
||||
({depth, _}) -> true;
|
||||
({certfile, _}) -> true;
|
||||
({keyfile, _}) -> true;
|
||||
({password, _}) -> true;
|
||||
({cacertfile, _}) -> true;
|
||||
({ciphers, _}) -> true;
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
Res = ssl:listen(Port, [binary,
|
||||
{packet, 0},
|
||||
{active, false},
|
||||
{nodelay, true} |
|
||||
SockOpts ++ SSLOpts]),
|
||||
case Res of
|
||||
{ok, ListenSocket} ->
|
||||
accept_ssl(ListenSocket, Module, Opts);
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Failed to open socket for ~p: ~p",
|
||||
[{Port, Module, Opts}, Reason]),
|
||||
error
|
||||
end.
|
||||
|
||||
accept_ssl(ListenSocket, Module, Opts) ->
|
||||
case ssl:accept(ListenSocket, 200) of
|
||||
@@ -95,7 +144,7 @@ accept_ssl(ListenSocket, Module, Opts) ->
|
||||
ok
|
||||
end,
|
||||
{ok, Pid} = Module:start({ssl, Socket}, Opts),
|
||||
ssl:controlling_process(Socket, Pid),
|
||||
catch ssl:controlling_process(Socket, Pid),
|
||||
accept_ssl(ListenSocket, Module, Opts);
|
||||
{error, timeout} ->
|
||||
accept_ssl(ListenSocket, Module, Opts);
|
||||
@@ -106,3 +155,39 @@ accept_ssl(ListenSocket, Module, Opts) ->
|
||||
end.
|
||||
|
||||
|
||||
start_listener(Port, Module, Opts) ->
|
||||
ChildSpec = {Port,
|
||||
{?MODULE, start, [Port, Module, Opts]},
|
||||
transient,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[?MODULE]},
|
||||
supervisor:start_child(ejabberd_listeners, ChildSpec).
|
||||
|
||||
stop_listener(Port) ->
|
||||
supervisor:terminate_child(ejabberd_listeners, Port),
|
||||
supervisor:delete_child(ejabberd_listeners, Port).
|
||||
|
||||
add_listener(Port, Module, Opts) ->
|
||||
Ports = case ejabberd_config:get_local_option(listen) of
|
||||
undefined ->
|
||||
[];
|
||||
Ls ->
|
||||
Ls
|
||||
end,
|
||||
Ports1 = lists:keydelete(Port, 1, Ports),
|
||||
Ports2 = [{Port, Module, Opts} | Ports1],
|
||||
ejabberd_config:add_local_option(listen, Ports2),
|
||||
start_listener(Port, Module, Opts).
|
||||
|
||||
delete_listener(Port) ->
|
||||
Ports = case ejabberd_config:get_local_option(listen) of
|
||||
undefined ->
|
||||
[];
|
||||
Ls ->
|
||||
Ls
|
||||
end,
|
||||
Ports1 = lists:keydelete(Port, 1, Ports),
|
||||
ejabberd_config:add_local_option(listen, Ports1),
|
||||
stop_listener(Port).
|
||||
|
||||
|
||||
+47
-51
@@ -14,7 +14,9 @@
|
||||
|
||||
-export([register_iq_handler/3,
|
||||
register_iq_handler/4,
|
||||
unregister_iq_handler/1
|
||||
unregister_iq_handler/1,
|
||||
refresh_iq_handlers/0,
|
||||
bounce_resource_packet/3
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -29,8 +31,10 @@ start_link() ->
|
||||
|
||||
init() ->
|
||||
MyDomain = ?MYNAME,
|
||||
ejabberd_router:register_local_route(MyDomain),
|
||||
ejabberd_router:register_route(MyDomain),
|
||||
catch ets:new(local_iqtable, [named_table, public]),
|
||||
ejabberd_hooks:add(local_send_to_resource_hook,
|
||||
?MODULE, bounce_resource_packet, 100),
|
||||
loop(#state{mydomain = MyDomain,
|
||||
iqtable = local_iqtable}).
|
||||
|
||||
@@ -47,11 +51,11 @@ loop(State) ->
|
||||
loop(State);
|
||||
{register_iq_handler, XMLNS, Module, Function} ->
|
||||
ets:insert(State#state.iqtable, {XMLNS, Module, Function}),
|
||||
mod_disco:register_feature(XMLNS),
|
||||
catch mod_disco:register_feature(XMLNS),
|
||||
loop(State);
|
||||
{register_iq_handler, XMLNS, Module, Function, Opts} ->
|
||||
ets:insert(State#state.iqtable, {XMLNS, Module, Function, Opts}),
|
||||
mod_disco:register_feature(XMLNS),
|
||||
catch mod_disco:register_feature(XMLNS),
|
||||
loop(State);
|
||||
{unregister_iq_handler, XMLNS} ->
|
||||
case ets:lookup(State#state.iqtable, XMLNS) of
|
||||
@@ -61,7 +65,20 @@ loop(State) ->
|
||||
ok
|
||||
end,
|
||||
ets:delete(State#state.iqtable, XMLNS),
|
||||
mod_disco:unregister_feature(XMLNS),
|
||||
catch mod_disco:unregister_feature(XMLNS),
|
||||
loop(State);
|
||||
refresh_iq_handlers ->
|
||||
lists:foreach(
|
||||
fun(T) ->
|
||||
case T of
|
||||
{XMLNS, _Module, _Function, _Opts} ->
|
||||
catch mod_disco:register_feature(XMLNS);
|
||||
{XMLNS, _Module, _Function} ->
|
||||
catch mod_disco:register_feature(XMLNS);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end, ets:tab2list(State#state.iqtable)),
|
||||
loop(State);
|
||||
_ ->
|
||||
loop(State)
|
||||
@@ -90,14 +107,8 @@ do_route(State, From, To, Packet) ->
|
||||
"error" -> ok;
|
||||
"result" -> ok;
|
||||
_ ->
|
||||
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
|
||||
ejabberd_hooks:run(local_send_to_resource_hook,
|
||||
[From, To, Packet])
|
||||
end;
|
||||
_ ->
|
||||
ejabberd_sm ! {route, From, To, Packet}
|
||||
@@ -106,31 +117,24 @@ do_route(State, From, To, Packet) ->
|
||||
process_iq(State, From, To, Packet) ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
{iq, _ID, Type, XMLNS, _SubEl} ->
|
||||
case jlib:is_iq_request_type(Type) of
|
||||
true ->
|
||||
case ets:lookup(State#state.iqtable, XMLNS) of
|
||||
[{_, Module, Function}] ->
|
||||
ResIQ = apply(Module, Function, [From, To, IQ]),
|
||||
if
|
||||
ResIQ /= ignore ->
|
||||
ejabberd_router ! {route,
|
||||
To,
|
||||
From,
|
||||
jlib:iq_to_xml(ResIQ)};
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:handle(Module, Function, Opts,
|
||||
From, To, IQ);
|
||||
[] ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
#iq{xmlns = XMLNS} ->
|
||||
case ets:lookup(State#state.iqtable, XMLNS) of
|
||||
[{_, Module, Function}] ->
|
||||
ResIQ = Module:Function(From, To, IQ),
|
||||
if
|
||||
ResIQ /= ignore ->
|
||||
ejabberd_router:route(
|
||||
To, From, jlib:iq_to_xml(ResIQ));
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:handle(Module, Function, Opts,
|
||||
From, To, IQ);
|
||||
[] ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
reply ->
|
||||
ok;
|
||||
@@ -149,18 +153,10 @@ register_iq_handler(XMLNS, Module, Fun, Opts) ->
|
||||
unregister_iq_handler(XMLNS) ->
|
||||
ejabberd_local ! {unregister_iq_handler, XMLNS}.
|
||||
|
||||
refresh_iq_handlers() ->
|
||||
ejabberd_local ! refresh_iq_handlers.
|
||||
|
||||
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.
|
||||
|
||||
bounce_resource_packet(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
stop.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
%% gen_event callbacks
|
||||
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
|
||||
code_change/3]).
|
||||
code_change/3, reopen_log/0]).
|
||||
|
||||
-record(state, {fd, file}).
|
||||
|
||||
@@ -89,6 +89,9 @@ terminate(_Reason, _State) ->
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
reopen_log() ->
|
||||
error_logger ! {emulator, noproc, reopen}.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
-export([start/3,
|
||||
receiver/4,
|
||||
change_shaper/2,
|
||||
reset_stream/1]).
|
||||
reset_stream/1,
|
||||
starttls/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
@@ -23,7 +24,7 @@ start(Socket, SockMod, Shaper) ->
|
||||
|
||||
|
||||
receiver(Socket, SockMod, Shaper, C2SPid) ->
|
||||
XMLStreamPid = xml_stream:start(C2SPid),
|
||||
XMLStreamPid = xml_stream:start(self(), C2SPid),
|
||||
ShaperState = shaper:new(Shaper),
|
||||
Timeout = case SockMod of
|
||||
ssl ->
|
||||
@@ -34,7 +35,30 @@ receiver(Socket, SockMod, Shaper, C2SPid) ->
|
||||
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid, Timeout).
|
||||
|
||||
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid, Timeout) ->
|
||||
case catch SockMod:recv(Socket, 0, Timeout) of
|
||||
Res = (catch SockMod:recv(Socket, 0, Timeout)),
|
||||
case Res of
|
||||
{ok, Data} ->
|
||||
receive
|
||||
{starttls, TLSSocket} ->
|
||||
exit(XMLStreamPid, closed),
|
||||
XMLStreamPid1 = xml_stream:start(self(), C2SPid),
|
||||
TLSRes = tls:recv_data(TLSSocket, Data),
|
||||
receiver1(TLSSocket, tls,
|
||||
ShaperState, C2SPid, XMLStreamPid1, Timeout,
|
||||
TLSRes)
|
||||
after 0 ->
|
||||
receiver1(Socket, SockMod,
|
||||
ShaperState, C2SPid, XMLStreamPid, Timeout,
|
||||
Res)
|
||||
end;
|
||||
_ ->
|
||||
receiver1(Socket, SockMod,
|
||||
ShaperState, C2SPid, XMLStreamPid, Timeout, Res)
|
||||
end.
|
||||
|
||||
|
||||
receiver1(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid, Timeout, Res) ->
|
||||
case Res of
|
||||
{ok, Text} ->
|
||||
ShaperSt1 = receive
|
||||
{change_shaper, Shaper} ->
|
||||
@@ -46,7 +70,7 @@ receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid, Timeout) ->
|
||||
XMLStreamPid1 = receive
|
||||
reset_stream ->
|
||||
exit(XMLStreamPid, closed),
|
||||
xml_stream:start(C2SPid)
|
||||
xml_stream:start(self(), C2SPid)
|
||||
after 0 ->
|
||||
XMLStreamPid
|
||||
end,
|
||||
@@ -75,4 +99,7 @@ change_shaper(Pid, Shaper) ->
|
||||
reset_stream(Pid) ->
|
||||
Pid ! reset_stream.
|
||||
|
||||
starttls(Pid, TLSSocket) ->
|
||||
Pid ! {starttls, TLSSocket}.
|
||||
|
||||
|
||||
|
||||
+59
-70
@@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_router.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Purpose : Main router
|
||||
%%% Created : 27 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -12,9 +12,7 @@
|
||||
|
||||
-export([route/3,
|
||||
register_route/1,
|
||||
register_local_route/1,
|
||||
unregister_route/1,
|
||||
unregister_local_route/1,
|
||||
dirty_get_all_routes/0,
|
||||
dirty_get_all_domains/0
|
||||
]).
|
||||
@@ -24,25 +22,23 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(route, {domain, node, pid}).
|
||||
-record(local_route, {domain, pid}).
|
||||
-record(route, {domain, pid}).
|
||||
|
||||
|
||||
start_link() ->
|
||||
{ok, proc_lib:spawn_link(ejabberd_router, init, [])}.
|
||||
Pid = proc_lib:spawn_link(ejabberd_router, init, []),
|
||||
register(ejabberd_router, Pid),
|
||||
{ok, Pid}.
|
||||
|
||||
init() ->
|
||||
register(ejabberd_router, self()),
|
||||
update_tables(),
|
||||
mnesia:create_table(route,
|
||||
[{ram_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes,
|
||||
record_info(fields, route)}]),
|
||||
mnesia:create_table(local_route,
|
||||
[{ram_copies, [node()]},
|
||||
{local_content, true},
|
||||
{attributes,
|
||||
record_info(fields, local_route)}]),
|
||||
mnesia:add_table_copy(local_route, node(), ram_copies),
|
||||
mnesia:add_table_copy(route, node(), ram_copies),
|
||||
mnesia:subscribe({table, route, simple}),
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
@@ -56,44 +52,33 @@ loop() ->
|
||||
ok
|
||||
end,
|
||||
loop();
|
||||
{register_route, Domain, Pid, Node} ->
|
||||
{register_route, Domain, Pid} ->
|
||||
F = fun() ->
|
||||
%case mnesia:wread({route, Domain}) of
|
||||
% [] ->
|
||||
% ok;
|
||||
% [Old] ->
|
||||
% % TODO: notify
|
||||
% ok
|
||||
%end,
|
||||
mnesia:write(#route{domain = Domain,
|
||||
node = Node,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
loop();
|
||||
{register_local_route, Domain, Pid} ->
|
||||
{unregister_route, Domain, Pid} ->
|
||||
F = fun() ->
|
||||
mnesia:write(#local_route{domain = Domain,
|
||||
pid = Pid})
|
||||
mnesia:delete_object(#route{domain = Domain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
loop();
|
||||
{unregister_route, Domain} ->
|
||||
F = fun() ->
|
||||
%case mnesia:wread({route, Domain}) of
|
||||
% [] ->
|
||||
% ok;
|
||||
% [Old] ->
|
||||
% % TODO: notify
|
||||
% ok
|
||||
%end,
|
||||
mnesia:delete({route, Domain})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
{mnesia_table_event, {write, #route{pid = Pid}, _ActivityId}} ->
|
||||
erlang:monitor(process, Pid),
|
||||
loop();
|
||||
{unregister_local_route, Domain} ->
|
||||
{'DOWN', _Ref, _Type, Pid, _Info} ->
|
||||
F = fun() ->
|
||||
mnesia:delete({local_route, Domain})
|
||||
Es = mnesia:select(
|
||||
route,
|
||||
[{#route{pid = Pid, _ = '_'},
|
||||
[],
|
||||
['$_']}]),
|
||||
lists:foreach(fun(E) ->
|
||||
mnesia:delete_object(E)
|
||||
end, Es)
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
loop();
|
||||
@@ -103,26 +88,21 @@ loop() ->
|
||||
|
||||
do_route(From, To, Packet) ->
|
||||
?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n", [From, To, Packet]),
|
||||
#jid{lserver = LDstDomain} = To,
|
||||
case mnesia:dirty_read({local_route, LDstDomain}) of
|
||||
LDstDomain = To#jid.lserver,
|
||||
case mnesia:dirty_read(route, LDstDomain) of
|
||||
[] ->
|
||||
case mnesia:dirty_read({route, LDstDomain}) of
|
||||
[] ->
|
||||
ejabberd_s2s ! {route, From, To, Packet};
|
||||
[R] ->
|
||||
Node = R#route.node,
|
||||
case node() of
|
||||
Node ->
|
||||
Pid = R#route.pid,
|
||||
?DEBUG("routed to process ~p~n", [Pid]),
|
||||
Pid ! {route, From, To, Packet};
|
||||
_ ->
|
||||
?DEBUG("routed to node ~p~n", [Node]),
|
||||
{ejabberd_router, Node} ! {route, From, To, Packet}
|
||||
end
|
||||
end;
|
||||
ejabberd_s2s ! {route, From, To, Packet};
|
||||
[R] ->
|
||||
Pid = R#local_route.pid,
|
||||
Pid = R#route.pid,
|
||||
?DEBUG("routed to process ~p~n", [Pid]),
|
||||
Pid ! {route, From, To, Packet};
|
||||
Rs ->
|
||||
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.
|
||||
@@ -132,24 +112,33 @@ route(From, To, Packet) ->
|
||||
ejabberd_router ! {route, From, To, Packet}.
|
||||
|
||||
register_route(Domain) ->
|
||||
ejabberd_router ! {register_route, Domain, self(), node()}.
|
||||
|
||||
register_local_route(Domain) ->
|
||||
ejabberd_router ! {register_local_route, Domain, self()}.
|
||||
ejabberd_router ! {register_route, Domain, self()}.
|
||||
|
||||
unregister_route(Domain) ->
|
||||
ejabberd_router ! {unregister_route, Domain}.
|
||||
|
||||
unregister_local_route(Domain) ->
|
||||
ejabberd_router ! {unregister_local_route, Domain}.
|
||||
ejabberd_router ! {unregister_route, Domain, self()}.
|
||||
|
||||
|
||||
dirty_get_all_routes() ->
|
||||
lists:delete(?MYNAME,
|
||||
lists:umerge(lists:sort(mnesia:dirty_all_keys(route)),
|
||||
lists:sort(mnesia:dirty_all_keys(local_route)))).
|
||||
lists:delete(?MYNAME, lists:usort(mnesia:dirty_all_keys(route))).
|
||||
|
||||
dirty_get_all_domains() ->
|
||||
lists:umerge(lists:sort(mnesia:dirty_all_keys(route)),
|
||||
lists:sort(mnesia:dirty_all_keys(local_route))).
|
||||
lists:usort(mnesia:dirty_all_keys(route)).
|
||||
|
||||
|
||||
|
||||
update_tables() ->
|
||||
case catch mnesia:table_info(route, attributes) of
|
||||
[domain, node, pid] ->
|
||||
mnesia:delete_table(route);
|
||||
[domain, pid] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
end,
|
||||
case lists:member(local_route, mnesia:system_info(tables)) of
|
||||
true ->
|
||||
mnesia:delete_table(local_route);
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
+57
-94
@@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_s2s.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Purpose : S2S connections manager
|
||||
%%% Created : 7 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -16,49 +16,38 @@
|
||||
try_register/1,
|
||||
dirty_get_connections/0]).
|
||||
|
||||
-include_lib("mnemosyne/include/mnemosyne.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(s2s, {fromto, node, key}).
|
||||
-record(local_s2s, {fromto, pid}).
|
||||
-record(s2s, {fromto, pid, key}).
|
||||
|
||||
|
||||
start_link() ->
|
||||
{ok, proc_lib:spawn_link(ejabberd_s2s, init, [])}.
|
||||
Pid = proc_lib:spawn_link(ejabberd_s2s, init, []),
|
||||
register(ejabberd_s2s, Pid),
|
||||
{ok, Pid}.
|
||||
|
||||
init() ->
|
||||
register(ejabberd_s2s, self()),
|
||||
update_tables(),
|
||||
mnesia:create_table(s2s,[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, s2s)}]),
|
||||
mnesia:add_table_index(session, node),
|
||||
mnesia:create_table(local_s2s,
|
||||
[{ram_copies, [node()]},
|
||||
{local_content, true},
|
||||
{attributes, record_info(fields, local_s2s)}]),
|
||||
mnesia:add_table_copy(local_s2s, node(), ram_copies),
|
||||
mnesia:add_table_copy(s2s, node(), ram_copies),
|
||||
mnesia:subscribe(system),
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
receive
|
||||
%{open_connection, User, Resource, From} ->
|
||||
% replace_and_register_my_connection(User, Resource, From),
|
||||
% replace_alien_connection(User, Resource),
|
||||
% loop();
|
||||
{closed_conection, FromTo} ->
|
||||
remove_connection(FromTo),
|
||||
loop();
|
||||
%{replace, User, Resource} ->
|
||||
% replace_my_connection(User, Resource),
|
||||
% loop();
|
||||
{mnesia_system_event, {mnesia_down, Node}} ->
|
||||
clean_table_from_bad_node(Node),
|
||||
loop();
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
@@ -68,16 +57,9 @@ loop() ->
|
||||
end.
|
||||
|
||||
|
||||
%open_session(User, Resource) ->
|
||||
% ejabberd_s2s ! {open_session, User, Resource, self()}.
|
||||
%
|
||||
%close_session(User, Resource) ->
|
||||
% ejabberd_s2s ! {close_session, User, Resource}.
|
||||
|
||||
|
||||
remove_connection(FromTo) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({local_s2s, FromTo}),
|
||||
mnesia:delete({s2s, FromTo})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
@@ -86,24 +68,17 @@ remove_connection(FromTo) ->
|
||||
|
||||
clean_table_from_bad_node(Node) ->
|
||||
F = fun() ->
|
||||
Es = mnesia:index_read(s2s, Node, #s2s.node),
|
||||
Es = mnesia:select(
|
||||
s2s,
|
||||
[{#s2s{pid = '$1', _ = '_'},
|
||||
[{'==', {node, '$1'}, Node}],
|
||||
['$_']}]),
|
||||
lists:foreach(fun(E) ->
|
||||
mnesia:delete_object(s2s, E, write)
|
||||
mnesia:delete_object(E)
|
||||
end, Es)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
%have_connection(FromTo) ->
|
||||
% F = fun() ->
|
||||
% [E] = mnesia:read({s2s, FromTo})
|
||||
% end,
|
||||
% case mnesia:transaction(F) of
|
||||
% {atomic, _} ->
|
||||
% true;
|
||||
% _ ->
|
||||
% false
|
||||
% end.
|
||||
|
||||
have_connection(FromTo) ->
|
||||
case catch mnesia:dirty_read(s2s, FromTo) of
|
||||
[_] ->
|
||||
@@ -126,10 +101,8 @@ try_register(FromTo) ->
|
||||
case mnesia:read({s2s, FromTo}) of
|
||||
[] ->
|
||||
mnesia:write(#s2s{fromto = FromTo,
|
||||
node = node(),
|
||||
pid = self(),
|
||||
key = Key}),
|
||||
mnesia:write(#local_s2s{fromto = FromTo,
|
||||
pid = self()}),
|
||||
{key, Key};
|
||||
_ ->
|
||||
false
|
||||
@@ -149,12 +122,8 @@ try_register(FromTo) ->
|
||||
do_route(From, To, Packet) ->
|
||||
?DEBUG("s2s manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
|
||||
[From, To, Packet, 8]),
|
||||
#jid{lserver = MyServer} = From,
|
||||
#jid{lserver = Server} = To,
|
||||
FromTo = {MyServer, Server},
|
||||
Key = randoms:get_string(),
|
||||
case find_connection(FromTo, Key) of
|
||||
{atomic, {local, Pid}} ->
|
||||
case find_connection(From, To) of
|
||||
{atomic, Pid} when pid(Pid) ->
|
||||
?DEBUG("sending to process ~p~n", [Pid]),
|
||||
% TODO
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
@@ -163,60 +132,36 @@ do_route(From, To, Packet) ->
|
||||
Attrs),
|
||||
send_element(Pid, {xmlelement, Name, NewAttrs, Els}),
|
||||
ok;
|
||||
{atomic, {remote, Node}} ->
|
||||
?DEBUG("sending to node ~p~n", [Node]),
|
||||
{ejabberd_s2s, Node} ! {route, From, To, Packet},
|
||||
ok;
|
||||
{atomic, new} ->
|
||||
?DEBUG("starting new s2s connection~n", []),
|
||||
{ok, Pid} = ejabberd_s2s_out:start(MyServer, Server, {new, Key}),
|
||||
mnesia:transaction(fun() ->
|
||||
mnesia:write(#local_s2s{fromto = FromTo,
|
||||
pid = Pid})
|
||||
end),
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
NewAttrs = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
send_element(Pid, {xmlelement, Name, NewAttrs, Els}),
|
||||
ok;
|
||||
{atomic, not_exists} ->
|
||||
?DEBUG("packet droped~n", []),
|
||||
ok;
|
||||
{aborted, Reason} ->
|
||||
?DEBUG("delivery failed: ~p~n", [Reason]),
|
||||
false
|
||||
end.
|
||||
|
||||
find_connection(FromTo, Key) ->
|
||||
F = fun() ->
|
||||
case mnesia:read({local_s2s, FromTo}) of
|
||||
[] ->
|
||||
case mnesia:read({s2s, FromTo}) of
|
||||
[Er] ->
|
||||
{remote, Er#s2s.node};
|
||||
[] ->
|
||||
mnesia:write(#s2s{fromto = FromTo,
|
||||
node = node(),
|
||||
key = Key}),
|
||||
new
|
||||
end;
|
||||
[El] ->
|
||||
{local, El#local_s2s.pid}
|
||||
end
|
||||
end,
|
||||
case catch mnesia:dirty_read({local_s2s, FromTo}) of
|
||||
find_connection(From, To) ->
|
||||
#jid{lserver = MyServer} = From,
|
||||
#jid{lserver = Server} = To,
|
||||
FromTo = {MyServer, Server},
|
||||
case catch mnesia:dirty_read(s2s, FromTo) of
|
||||
{'EXIT', Reason} ->
|
||||
{aborted, Reason};
|
||||
[] ->
|
||||
case catch mnesia:dirty_read({s2s, FromTo}) of
|
||||
[Er] ->
|
||||
{atomic, {remote, Er#s2s.node}};
|
||||
[] ->
|
||||
mnesia:transaction(F)
|
||||
end;
|
||||
?DEBUG("starting new s2s connection~n", []),
|
||||
Key = randoms:get_string(),
|
||||
{ok, Pid} = ejabberd_s2s_out:start(MyServer, Server, {new, Key}),
|
||||
F = fun() ->
|
||||
case mnesia:read({s2s, FromTo}) of
|
||||
[El] ->
|
||||
El#s2s.pid;
|
||||
[] ->
|
||||
mnesia:write(#s2s{fromto = FromTo,
|
||||
pid = Pid,
|
||||
key = Key}),
|
||||
Pid
|
||||
end
|
||||
end,
|
||||
mnesia:transaction(F);
|
||||
[El] ->
|
||||
{atomic, {local, El#local_s2s.pid}}
|
||||
{atomic, El#s2s.pid}
|
||||
end.
|
||||
|
||||
|
||||
@@ -227,3 +172,21 @@ send_element(Pid, El) ->
|
||||
dirty_get_connections() ->
|
||||
mnesia:dirty_all_keys(s2s).
|
||||
|
||||
|
||||
update_tables() ->
|
||||
case catch mnesia:table_info(s2s, attributes) of
|
||||
[fromto, node, key] ->
|
||||
mnesia:transform_table(s2s, ignore, [fromto, pid, key]),
|
||||
mnesia:clear_table(s2s);
|
||||
[fromto, pid, key] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
end,
|
||||
case lists:member(local_s2s, mnesia:system_info(tables)) of
|
||||
true ->
|
||||
mnesia:delete_table(local_s2s);
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
+32
-14
@@ -37,7 +37,8 @@
|
||||
receiver,
|
||||
streamid,
|
||||
shaper,
|
||||
connections = ?DICT:new()}).
|
||||
connections = ?DICT:new(),
|
||||
timer}).
|
||||
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
@@ -95,12 +96,13 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
{value, {_, S}} -> S;
|
||||
_ -> none
|
||||
end,
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
{ok, wait_for_stream,
|
||||
#state{socket = Socket,
|
||||
receiver = ReceiverPid,
|
||||
streamid = new_id(),
|
||||
shaper = Shaper},
|
||||
?S2STIMEOUT}.
|
||||
shaper = Shaper,
|
||||
timer = Timer}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
@@ -114,7 +116,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
case {xml:get_attr_s("xmlns", Attrs), xml:get_attr_s("xmlns:db", Attrs)} of
|
||||
{"jabber:server", "jabber:server:dialback"} ->
|
||||
send_text(StateData#state.socket, ?STREAM_HEADER),
|
||||
{next_state, stream_established, StateData#state{}, ?S2STIMEOUT};
|
||||
{next_state, stream_established, StateData#state{}};
|
||||
_ ->
|
||||
send_text(StateData#state.socket, ?INVALID_NAMESPACE_ERR),
|
||||
{stop, normal, StateData}
|
||||
@@ -132,6 +134,8 @@ wait_for_stream(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
stream_established({xmlstreamelement, El}, StateData) ->
|
||||
cancel_timer(StateData#state.timer),
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
case is_key_packet(El) of
|
||||
{key, To, From, Id, Key} ->
|
||||
?INFO_MSG("GET KEY: ~p", [{To, From, Id, Key}]),
|
||||
@@ -147,8 +151,8 @@ stream_established({xmlstreamelement, El}, StateData) ->
|
||||
change_shaper(StateData, jlib:make_jid("", LFrom, "")),
|
||||
{next_state,
|
||||
stream_established,
|
||||
StateData#state{connections = Conns},
|
||||
?S2STIMEOUT};
|
||||
StateData#state{connections = Conns,
|
||||
timer = Timer}};
|
||||
_ ->
|
||||
send_text(StateData#state.socket, ?HOST_UNKNOWN_ERR),
|
||||
{stop, normal, StateData}
|
||||
@@ -169,7 +173,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
|
||||
{"id", Id},
|
||||
{"type", Type}],
|
||||
[]}),
|
||||
{next_state, wait_for_key, StateData, ?S2STIMEOUT};
|
||||
{next_state, stream_established, StateData#state{timer = Timer}};
|
||||
_ ->
|
||||
{xmlelement, Name, Attrs, _Els} = El,
|
||||
From_s = xml:get_attr_s("from", Attrs),
|
||||
@@ -195,9 +199,9 @@ stream_established({xmlstreamelement, El}, StateData) ->
|
||||
end;
|
||||
true ->
|
||||
error
|
||||
end
|
||||
end,
|
||||
{next_state, stream_established, StateData, ?S2STIMEOUT};
|
||||
end,
|
||||
{next_state, stream_established, StateData#state{timer = Timer}}
|
||||
end;
|
||||
|
||||
stream_established({valid, From, To}, StateData) ->
|
||||
send_element(StateData#state.socket,
|
||||
@@ -212,7 +216,7 @@ stream_established({valid, From, To}, StateData) ->
|
||||
NSD = StateData#state{
|
||||
connections = ?DICT:store({LFrom, LTo}, established,
|
||||
StateData#state.connections)},
|
||||
{next_state, stream_established, NSD, ?S2STIMEOUT};
|
||||
{next_state, stream_established, NSD};
|
||||
|
||||
stream_established({invalid, From, To}, StateData) ->
|
||||
send_element(StateData#state.socket,
|
||||
@@ -225,9 +229,9 @@ stream_established({invalid, From, To}, StateData) ->
|
||||
LFrom = jlib:nameprep(From),
|
||||
LTo = jlib:nameprep(To),
|
||||
NSD = StateData#state{
|
||||
connections = ?DICT:store({LFrom, LTo}, established,
|
||||
connections = ?DICT:erase({LFrom, LTo},
|
||||
StateData#state.connections)},
|
||||
{next_state, stream_established, NSD, ?S2STIMEOUT};
|
||||
{next_state, stream_established, NSD};
|
||||
|
||||
stream_established({xmlstreamend, _Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
@@ -265,7 +269,7 @@ stream_established(closed, StateData) ->
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData, ?S2STIMEOUT}.
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
@@ -292,6 +296,11 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
handle_info({send_text, Text}, StateName, StateData) ->
|
||||
send_text(StateData#state.socket, Text),
|
||||
{next_state, StateName, StateData};
|
||||
|
||||
handle_info({timeout, Timer, _}, StateName,
|
||||
#state{timer = Timer} = StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
@@ -333,6 +342,15 @@ change_shaper(StateData, JID) ->
|
||||
new_id() ->
|
||||
randoms:get_string().
|
||||
|
||||
cancel_timer(Timer) ->
|
||||
erlang:cancel_timer(Timer),
|
||||
receive
|
||||
{timeout, Timer, _} ->
|
||||
ok
|
||||
after 0 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
|
||||
{key,
|
||||
|
||||
+81
-28
@@ -32,7 +32,8 @@
|
||||
|
||||
-record(state, {socket, receiver, streamid,
|
||||
myname, server, xmlpid, queue,
|
||||
new = false, verify = false}).
|
||||
new = false, verify = false,
|
||||
timer}).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
@@ -91,12 +92,13 @@ init([From, Server, Type]) ->
|
||||
{verify, Pid, Key, SID} ->
|
||||
{false, {Pid, Key, SID}}
|
||||
end,
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
{ok, open_socket, #state{queue = queue:new(),
|
||||
myname = From,
|
||||
server = Server,
|
||||
new = New,
|
||||
verify = Verify},
|
||||
?S2STIMEOUT}.
|
||||
verify = Verify,
|
||||
timer = Timer}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
@@ -106,14 +108,18 @@ init([From, Server, Type]) ->
|
||||
%%----------------------------------------------------------------------
|
||||
open_socket(init, StateData) ->
|
||||
{Addr, Port} = get_addr_port(StateData#state.server),
|
||||
?DEBUG("s2s_out: connecting to ~s:~p~n", [Addr, Port]),
|
||||
Res = case gen_tcp:connect(Addr, Port,
|
||||
[binary, {packet, 0}]) of
|
||||
{ok, _Socket} = R -> R;
|
||||
{error, Reason1} ->
|
||||
?DEBUG("s2s_out: connect return ~p~n", [Reason1]),
|
||||
gen_tcp:connect(Addr, Port,
|
||||
[binary, {packet, 0}, inet6])
|
||||
Res = case idna:domain_utf8_to_ascii(Addr) of
|
||||
false -> {error, badarg};
|
||||
ASCIIAddr ->
|
||||
?DEBUG("s2s_out: connecting to ~s:~p~n", [ASCIIAddr, Port]),
|
||||
case gen_tcp:connect(ASCIIAddr, Port,
|
||||
[binary, {packet, 0}]) of
|
||||
{ok, _Socket} = R -> R;
|
||||
{error, Reason1} ->
|
||||
?DEBUG("s2s_out: connect return ~p~n", [Reason1]),
|
||||
catch gen_tcp:connect(Addr, Port,
|
||||
[binary, {packet, 0}, inet6])
|
||||
end
|
||||
end,
|
||||
case Res of
|
||||
{ok, Socket} ->
|
||||
@@ -123,12 +129,16 @@ open_socket(init, StateData) ->
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{socket = Socket,
|
||||
xmlpid = XMLStreamPid,
|
||||
streamid = new_id()},
|
||||
?S2STIMEOUT};
|
||||
streamid = new_id()}};
|
||||
{error, Reason} ->
|
||||
?DEBUG("s2s_out: inet6 connect return ~p~n", [Reason]),
|
||||
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
|
||||
bounce_messages(Error),
|
||||
{stop, normal, StateData};
|
||||
{'EXIT', Reason} ->
|
||||
?DEBUG("s2s_out: inet6 connect crashed ~p~n", [Reason]),
|
||||
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
|
||||
bounce_messages(Error),
|
||||
{stop, normal, StateData}
|
||||
end.
|
||||
|
||||
@@ -140,9 +150,8 @@ 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 ->
|
||||
@@ -174,8 +183,7 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
{"id", SID}],
|
||||
[{xmlcdata, Key2}]})
|
||||
end,
|
||||
{next_state, wait_for_validation,
|
||||
StateData#state{new = New}, ?S2STIMEOUT};
|
||||
{next_state, wait_for_validation, StateData#state{new = New}};
|
||||
_ ->
|
||||
send_text(StateData#state.socket, ?INVALID_NAMESPACE_ERR),
|
||||
{stop, normal, StateData}
|
||||
@@ -201,7 +209,8 @@ wait_for_validation({xmlstreamelement, El}, StateData) ->
|
||||
case Type of
|
||||
"valid" ->
|
||||
send_queue(StateData#state.socket, StateData#state.queue),
|
||||
{next_state, stream_established, StateData, ?S2STIMEOUT};
|
||||
{next_state, stream_established,
|
||||
StateData#state{queue = queue:new()}};
|
||||
_ ->
|
||||
% TODO: bounce packets
|
||||
{stop, normal, StateData}
|
||||
@@ -210,7 +219,7 @@ wait_for_validation({xmlstreamelement, El}, StateData) ->
|
||||
?INFO_MSG("recv verify: ~p", [{From, To, Id, Type}]),
|
||||
case StateData#state.verify of
|
||||
false ->
|
||||
{next_state, wait_for_validation, StateData, ?S2STIMEOUT};
|
||||
{next_state, wait_for_validation, StateData};
|
||||
{Pid, _Key, _SID} ->
|
||||
case Type of
|
||||
"valid" ->
|
||||
@@ -224,10 +233,15 @@ wait_for_validation({xmlstreamelement, El}, StateData) ->
|
||||
StateData#state.server,
|
||||
StateData#state.myname})
|
||||
end,
|
||||
{stop, normal, StateData}
|
||||
if
|
||||
StateData#state.verify == false ->
|
||||
{stop, normal, StateData};
|
||||
true ->
|
||||
{next_state, wait_for_validation, StateData}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
{next_state, wait_for_validation, StateData, ?S2STIMEOUT}
|
||||
{next_state, wait_for_validation, StateData}
|
||||
end;
|
||||
|
||||
wait_for_validation({xmlstreamend, Name}, StateData) ->
|
||||
@@ -270,7 +284,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{next_state, stream_established, StateData, ?S2STIMEOUT};
|
||||
{next_state, stream_established, StateData};
|
||||
|
||||
stream_established({xmlstreamend, Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
@@ -334,24 +348,40 @@ code_change(OldVsn, StateName, StateData, Extra) ->
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({send_text, Text}, StateName, StateData) ->
|
||||
send_text(StateData#state.socket, Text),
|
||||
{next_state, StateName, StateData};
|
||||
cancel_timer(StateData#state.timer),
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
{next_state, StateName, StateData#state{timer = Timer}};
|
||||
|
||||
handle_info({send_element, El}, StateName, StateData) ->
|
||||
cancel_timer(StateData#state.timer),
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
case StateName of
|
||||
stream_established ->
|
||||
send_element(StateData#state.socket, El),
|
||||
{next_state, StateName, StateData};
|
||||
{next_state, StateName, StateData#state{timer = Timer}};
|
||||
_ ->
|
||||
Q = queue:in(El, StateData#state.queue),
|
||||
{next_state, StateName, StateData#state{queue = Q}}
|
||||
{next_state, StateName, StateData#state{queue = Q,
|
||||
timer = Timer}}
|
||||
end;
|
||||
|
||||
handle_info({tcp, Socket, Data}, StateName, StateData) ->
|
||||
xml_stream:send_text(StateData#state.xmlpid, Data),
|
||||
{next_state, StateName, StateData};
|
||||
|
||||
handle_info({tcp_closed, Socket}, StateName, StateData) ->
|
||||
gen_fsm:send_event(self(), closed),
|
||||
{next_state, StateName, StateData};
|
||||
|
||||
handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
|
||||
gen_fsm:send_event(self(), closed),
|
||||
{next_state, StateName, StateData};
|
||||
|
||||
handle_info({timeout, Timer, _}, StateName,
|
||||
#state{timer = Timer} = StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
@@ -361,6 +391,8 @@ handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StateData) ->
|
||||
?INFO_MSG("terminated: ~p", [Reason]),
|
||||
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
|
||||
bounce_queue(StateData#state.queue, Error),
|
||||
case StateData#state.new of
|
||||
false ->
|
||||
ok;
|
||||
@@ -396,13 +428,34 @@ send_queue(Socket, Q) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
bounce_queue(Q, Error) ->
|
||||
case queue:out(Q) of
|
||||
{{value, El}, Q1} ->
|
||||
Err = jlib:make_error_reply(El, Error),
|
||||
From = jlib:string_to_jid(xml:get_tag_attr_s("from", El)),
|
||||
To = jlib:string_to_jid(xml:get_tag_attr_s("to", El)),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
bounce_queue(Q1, Error);
|
||||
{empty, Q1} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
new_id() ->
|
||||
randoms:get_string().
|
||||
|
||||
cancel_timer(Timer) ->
|
||||
erlang:cancel_timer(Timer),
|
||||
receive
|
||||
{timeout, Timer, _} ->
|
||||
ok
|
||||
after 0 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
bounce_messages(Error) ->
|
||||
receive
|
||||
{send_element, El} ->
|
||||
{xmlelement, Name, Attrs, SubTags} = El,
|
||||
{xmlelement, _Name, Attrs, _SubTags} = El,
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"error" ->
|
||||
ok;
|
||||
@@ -410,7 +463,7 @@ bounce_messages(Error) ->
|
||||
Err = jlib:make_error_reply(El, Error),
|
||||
From = jlib:string_to_jid(xml:get_attr_s("from", Attrs)),
|
||||
To = jlib:string_to_jid(xml:get_attr_s("to", Attrs)),
|
||||
ejabberd_router ! {route, To, From, Err}
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end,
|
||||
bounce_messages(Error)
|
||||
after 0 ->
|
||||
|
||||
+21
-12
@@ -30,7 +30,7 @@
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(state, {socket, receiver, streamid, sockmod,
|
||||
hosts, password}).
|
||||
hosts, password, access}).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
@@ -87,6 +87,10 @@ start_link(SockData, Opts) ->
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([{SockMod, Socket}, Opts]) ->
|
||||
Access = case lists:keysearch(access, 1, Opts) of
|
||||
{value, {_, A}} -> A;
|
||||
_ -> all
|
||||
end,
|
||||
{Hosts, Password} =
|
||||
case lists:keysearch(hosts, 1, Opts) of
|
||||
{value, {_, Hs, HOpts}} ->
|
||||
@@ -118,7 +122,8 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
streamid = new_id(),
|
||||
sockmod = SockMod,
|
||||
hosts = Hosts,
|
||||
password = Password
|
||||
password = Password,
|
||||
access = Access
|
||||
}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
@@ -127,8 +132,6 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
%state_name(Event, StateData) ->
|
||||
% {next_state, state_name, StateData}.
|
||||
|
||||
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
% TODO
|
||||
@@ -164,7 +167,7 @@ wait_for_handshake({xmlstreamelement, El}, StateData) ->
|
||||
send_text(StateData, "<handshake/>"),
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:register_local_route(H)
|
||||
ejabberd_router:register_route(H)
|
||||
end, StateData#state.hosts),
|
||||
{next_state, stream_established, StateData};
|
||||
_ ->
|
||||
@@ -280,12 +283,18 @@ handle_info({send_element, El}, StateName, StateData) ->
|
||||
send_element(StateData, El),
|
||||
{next_state, StateName, StateData};
|
||||
handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}),
|
||||
send_text(StateData, Text),
|
||||
case acl:match_rule(StateData#state.access, From) of
|
||||
allow ->
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}),
|
||||
send_text(StateData, Text);
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end,
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
|
||||
@@ -299,7 +308,7 @@ terminate(_Reason, StateName, StateData) ->
|
||||
stream_established ->
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:unregister_local_route(H)
|
||||
ejabberd_router:unregister_route(H)
|
||||
end, StateData#state.hosts);
|
||||
_ ->
|
||||
ok
|
||||
|
||||
+95
-106
@@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_sm.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Purpose : Session manager
|
||||
%%% Created : 24 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -11,9 +11,10 @@
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start_link/0, init/0, open_session/2, close_session/2,
|
||||
bounce_offline_message/3,
|
||||
get_user_resources/1,
|
||||
set_presence/3,
|
||||
unset_presence/2,
|
||||
unset_presence/3,
|
||||
dirty_get_sessions_list/0,
|
||||
dirty_get_my_sessions_list/0,
|
||||
register_iq_handler/3,
|
||||
@@ -21,34 +22,31 @@
|
||||
unregister_iq_handler/1
|
||||
]).
|
||||
|
||||
-include_lib("mnemosyne/include/mnemosyne.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(session, {ur, user, node}).
|
||||
-record(local_session, {ur, pid}).
|
||||
-record(session, {ur, user, pid}).
|
||||
-record(presence, {ur, user, priority}).
|
||||
|
||||
start_link() ->
|
||||
{ok, proc_lib:spawn_link(ejabberd_sm, init, [])}.
|
||||
Pid = proc_lib:spawn_link(ejabberd_sm, init, []),
|
||||
register(ejabberd_sm, Pid),
|
||||
{ok, Pid}.
|
||||
|
||||
init() ->
|
||||
register(ejabberd_sm, self()),
|
||||
update_tables(),
|
||||
mnesia:create_table(session, [{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, session)}]),
|
||||
mnesia:add_table_index(session, user),
|
||||
mnesia:add_table_index(session, node),
|
||||
mnesia:create_table(local_session,
|
||||
[{ram_copies, [node()]},
|
||||
{local_content, true},
|
||||
{attributes, record_info(fields, local_session)}]),
|
||||
mnesia:add_table_copy(local_session, node(), ram_copies),
|
||||
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, user),
|
||||
mnesia:subscribe(system),
|
||||
ets:new(sm_iqtable, [named_table]),
|
||||
ejabberd_hooks:add(offline_message_hook,
|
||||
ejabberd_sm, bounce_offline_message, 100),
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
@@ -68,9 +66,6 @@ loop() ->
|
||||
{close_session, User, Resource} ->
|
||||
remove_connection(User, Resource),
|
||||
loop();
|
||||
{replace, User, Resource} ->
|
||||
replace_my_connection(User, Resource),
|
||||
loop();
|
||||
{mnesia_system_event, {mnesia_down, Node}} ->
|
||||
clean_table_from_bad_node(Node),
|
||||
loop();
|
||||
@@ -88,7 +83,6 @@ loop() ->
|
||||
ok
|
||||
end,
|
||||
ets:delete(sm_iqtable, XMLNS),
|
||||
mod_disco:unregister_feature(XMLNS),
|
||||
loop();
|
||||
_ ->
|
||||
loop()
|
||||
@@ -108,46 +102,15 @@ register_connection(User, Resource, Pid) ->
|
||||
UR = {LUser, LResource},
|
||||
F = fun() ->
|
||||
Ss = mnesia:wread({session, UR}),
|
||||
Ls = mnesia:wread({local_session, UR}),
|
||||
mnesia:write(#session{ur = UR, user = LUser, node = node()}),
|
||||
mnesia:write(#local_session{ur = UR, pid = Pid}),
|
||||
{Ss, Ls}
|
||||
mnesia:write(#session{ur = UR, user = LUser, pid = Pid}),
|
||||
Ss
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, {Ss, Ls}} ->
|
||||
{atomic, Ss} ->
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
if R#session.node /= node() ->
|
||||
{ejabberd_sm, R#session.node} !
|
||||
{replace, User, Resource};
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, Ss),
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
R#local_session.pid ! replaced
|
||||
end, Ls);
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
replace_my_connection(User, Resource) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LResource = jlib:resourceprep(Resource),
|
||||
UR = {LUser, LResource},
|
||||
F = fun() ->
|
||||
Es = mnesia:read({local_session, UR}),
|
||||
mnesia:delete({local_session, UR}),
|
||||
Es
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, Rs} ->
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
R#local_session.pid ! replaced
|
||||
end, Rs);
|
||||
R#session.pid ! replaced
|
||||
end, Ss);
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
@@ -156,9 +119,8 @@ replace_my_connection(User, Resource) ->
|
||||
remove_connection(User, Resource) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LResource = jlib:resourceprep(Resource),
|
||||
UR = {LUser, LResource},
|
||||
F = fun() ->
|
||||
UR = {LUser, LResource},
|
||||
mnesia:delete({local_session, UR}),
|
||||
mnesia:delete({session, UR})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
@@ -166,11 +128,13 @@ remove_connection(User, Resource) ->
|
||||
|
||||
clean_table_from_bad_node(Node) ->
|
||||
F = fun() ->
|
||||
Es = mnesia:index_read(session, Node, #session.node),
|
||||
Es = mnesia:select(
|
||||
session,
|
||||
[{#session{pid = '$1', _ = '_'},
|
||||
[{'==', {node, '$1'}, Node}],
|
||||
['$_']}]),
|
||||
lists:foreach(fun(E) ->
|
||||
mnesia:delete_object(session, E, write),
|
||||
mnesia:delete(
|
||||
{user_resource, E#session.ur})
|
||||
mnesia:delete_object(E)
|
||||
end, Es)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
@@ -188,27 +152,26 @@ do_route(From, To, Packet) ->
|
||||
"" ->
|
||||
case Name of
|
||||
"presence" ->
|
||||
FromU = jlib:jid_replace_resource(From, ""),
|
||||
{Pass, Subsc} =
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"subscribe" ->
|
||||
{mod_roster:in_subscription(User,
|
||||
FromU,
|
||||
From,
|
||||
subscribe),
|
||||
true};
|
||||
"subscribed" ->
|
||||
{mod_roster:in_subscription(User,
|
||||
FromU,
|
||||
From,
|
||||
subscribed),
|
||||
true};
|
||||
"unsubscribe" ->
|
||||
{mod_roster:in_subscription(User,
|
||||
FromU,
|
||||
From,
|
||||
unsubscribe),
|
||||
true};
|
||||
"unsubscribed" ->
|
||||
{mod_roster:in_subscription(User,
|
||||
FromU,
|
||||
From,
|
||||
unsubscribed),
|
||||
true};
|
||||
_ ->
|
||||
@@ -216,11 +179,11 @@ do_route(From, To, Packet) ->
|
||||
end,
|
||||
if Pass ->
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
Resources = get_user_resources(User),
|
||||
PResources = get_user_present_resources(User),
|
||||
if
|
||||
Resources /= [] ->
|
||||
PResources /= [] ->
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
fun({_, R}) ->
|
||||
if LFrom /=
|
||||
{LUser, LServer, R} ->
|
||||
ejabberd_sm !
|
||||
@@ -231,12 +194,20 @@ do_route(From, To, Packet) ->
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, Resources);
|
||||
end, PResources);
|
||||
true ->
|
||||
if
|
||||
Subsc ->
|
||||
catch mod_offline:store_packet(
|
||||
From, To, Packet);
|
||||
case ejabberd_auth:is_user_exists(LUser) of
|
||||
true ->
|
||||
ejabberd_hooks:run(
|
||||
offline_subscription_hook,
|
||||
[From, To, Packet]);
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
@@ -261,8 +232,7 @@ do_route(From, To, Packet) ->
|
||||
end;
|
||||
_ ->
|
||||
LUR = {LUser, LResource},
|
||||
Sess = mnesia:dirty_read({session, LUR}),
|
||||
case Sess of
|
||||
case mnesia:dirty_read({session, LUR}) of
|
||||
[] ->
|
||||
case Name of
|
||||
"message" ->
|
||||
@@ -280,52 +250,49 @@ do_route(From, To, Packet) ->
|
||||
_ ->
|
||||
?DEBUG("packet droped~n", [])
|
||||
end;
|
||||
[Ses] ->
|
||||
case mnesia:dirty_read({local_session, LUR}) of
|
||||
[] ->
|
||||
Node = Ses#session.node,
|
||||
?DEBUG("sending to node ~p~n", [Node]),
|
||||
{ejabberd_sm, Node} ! {route, From, To, Packet};
|
||||
[El] ->
|
||||
Pid = El#local_session.pid,
|
||||
?DEBUG("sending to process ~p~n", [Pid]),
|
||||
Pid ! {route, From, To, Packet}
|
||||
end
|
||||
[Sess] ->
|
||||
Pid = Sess#session.pid,
|
||||
?DEBUG("sending to process ~p~n", [Pid]),
|
||||
Pid ! {route, From, To, Packet}
|
||||
end
|
||||
end.
|
||||
|
||||
route_message(From, To, Packet) ->
|
||||
#jid{luser = LUser} = To,
|
||||
LUser = To#jid.luser,
|
||||
case catch lists:max(get_user_present_resources(LUser)) of
|
||||
{'EXIT', _} ->
|
||||
{Priority, R} when is_integer(Priority),
|
||||
Priority >= 0 ->
|
||||
LResource = jlib:resourceprep(R),
|
||||
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) 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;
|
||||
ejabberd_hooks:run(offline_message_hook,
|
||||
[From, To, Packet]);
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end
|
||||
end;
|
||||
{_, R} ->
|
||||
ejabberd_sm ! {route,
|
||||
From,
|
||||
jlib:jid_replace_resource(To, R),
|
||||
Packet}
|
||||
end
|
||||
end.
|
||||
|
||||
bounce_offline_message(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
stop.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
@@ -352,14 +319,14 @@ set_presence(User, Resource, Priority) ->
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
unset_presence(User, Resource) ->
|
||||
unset_presence(User, Resource, Status) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
UR = {User, Resource},
|
||||
mnesia:delete({presence, UR})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
catch mod_last:on_presence_update(LUser).
|
||||
catch mod_last:on_presence_update(LUser, Status).
|
||||
|
||||
get_user_present_resources(LUser) ->
|
||||
case catch mnesia:dirty_index_read(presence, LUser, #presence.user) of
|
||||
@@ -375,7 +342,11 @@ dirty_get_sessions_list() ->
|
||||
mnesia:dirty_all_keys(session).
|
||||
|
||||
dirty_get_my_sessions_list() ->
|
||||
mnesia:dirty_all_keys(local_session).
|
||||
mnesia:dirty_select(
|
||||
session,
|
||||
[{#session{pid = '$1', _ = '_'},
|
||||
[{'==', {node, '$1'}, node()}],
|
||||
['$_']}]).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
@@ -383,7 +354,7 @@ dirty_get_my_sessions_list() ->
|
||||
process_iq(From, To, Packet) ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
{iq, _ID, _Type, XMLNS, _SubEl} ->
|
||||
#iq{xmlns = XMLNS} ->
|
||||
case ets:lookup(sm_iqtable, XMLNS) of
|
||||
[{_, Module, Function}] ->
|
||||
ResIQ = Module:Function(From, To, IQ),
|
||||
@@ -408,7 +379,7 @@ process_iq(From, To, Packet) ->
|
||||
ok;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST),
|
||||
ejabberd_router ! {route, To, From, Err},
|
||||
ejabberd_router:route(To, From, Err),
|
||||
ok
|
||||
end.
|
||||
|
||||
@@ -421,3 +392,21 @@ register_iq_handler(XMLNS, Module, Fun, Opts) ->
|
||||
unregister_iq_handler(XMLNS) ->
|
||||
ejabberd_sm ! {unregister_iq_handler, XMLNS}.
|
||||
|
||||
|
||||
|
||||
update_tables() ->
|
||||
case catch mnesia:table_info(session, attributes) of
|
||||
[ur, user, node] ->
|
||||
mnesia:delete_table(session);
|
||||
[ur, user, pid] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
end,
|
||||
case lists:member(local_session, mnesia:system_info(tables)) of
|
||||
true ->
|
||||
mnesia:delete_table(local_session);
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
+65
-31
@@ -19,36 +19,48 @@ start_link() ->
|
||||
|
||||
|
||||
init([]) ->
|
||||
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]},
|
||||
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, []},
|
||||
@@ -87,6 +99,22 @@ init([]) ->
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
HTTPSupervisor =
|
||||
{ejabberd_http_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_http_sup, ejabberd_http]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
HTTPPollSupervisor =
|
||||
{ejabberd_http_poll_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_http_poll_sup, ejabberd_http_poll]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
IQSupervisor =
|
||||
{ejabberd_iq_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
@@ -96,12 +124,18 @@ init([]) ->
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
{ok, {{one_for_one, 10, 1},
|
||||
[Router, SM, S2S, Local,
|
||||
[Hooks,
|
||||
Router,
|
||||
SM,
|
||||
S2S,
|
||||
Local,
|
||||
StringPrep,
|
||||
C2SSupervisor,
|
||||
S2SInSupervisor,
|
||||
S2SOutSupervisor,
|
||||
ServiceSupervisor,
|
||||
HTTPSupervisor,
|
||||
HTTPPollSupervisor,
|
||||
IQSupervisor,
|
||||
Listener]}}.
|
||||
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@
|
||||
LIBS = @LIBS@ @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
@@ -29,8 +24,15 @@ $(OUTDIR)/%.beam: %.erl ELDAPv3.erl
|
||||
|
||||
|
||||
clean:
|
||||
rm -f ELDAPv3.asn1db
|
||||
rm -f ELDAPv3.erl
|
||||
rm -f ELDAPv3.hrl
|
||||
rm -f ELDAPv3.beam
|
||||
rm -f $(OBJS)
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
include ..\Makefile.inc
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
|
||||
OBJS = \
|
||||
$(OUTDIR)\eldap.beam \
|
||||
$(OUTDIR)\ELDAPv3.beam
|
||||
|
||||
ALL : $(OBJS)
|
||||
|
||||
CLEAN :
|
||||
-@erase ELDAPv3.asn1db
|
||||
-@erase ELDAPv3.erl
|
||||
-@erase ELDAPv3.hrl
|
||||
-@erase ELDAPv3.beam
|
||||
-@erase $(OBJS)
|
||||
|
||||
ELDAPv3.erl : ELDAPv3.asn
|
||||
erlc -bber_bin -W $(EFLAGS) ELDAPv3.asn
|
||||
|
||||
$(OUTDIR)\eldap.beam : eldap.erl ELDAPv3.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) eldap.erl
|
||||
|
||||
$(OUTDIR)\ELDAPv3.beam : ELDAPv3.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) ELDAPv3.erl
|
||||
+1
-1
@@ -765,7 +765,7 @@ retry_connect() ->
|
||||
cmd_timeout(Timer, Id, S) ->
|
||||
Dict = S#eldap.dict,
|
||||
case dict:find(Id, Dict) of
|
||||
{ok, [{Id, Timer, From, Name}|Res]} ->
|
||||
{ok, [{Timer, From, Name}|Res]} ->
|
||||
case Name of
|
||||
searchRequest ->
|
||||
{Res1, Ref1} = polish(Res),
|
||||
|
||||
+2
-4
@@ -1,6 +1,7 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <erl_driver.h>
|
||||
#include <ei.h>
|
||||
#include <expat.h>
|
||||
@@ -242,12 +243,9 @@ ErlDrvEntry expat_driver_entry = {
|
||||
NULL /* F_PTR outputv, reserved */
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
DRIVER_INIT(expat_erl) /* must match name in driver_entry */
|
||||
{
|
||||
return &expat_driver_entry;
|
||||
return &expat_driver_entry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% 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.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
stop_module/1,
|
||||
get_opt/2,
|
||||
get_opt/3,
|
||||
get_module_opt/3,
|
||||
loaded_modules/0]).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
@@ -73,6 +74,16 @@ get_opt(Opt, Opts, Default) ->
|
||||
Val
|
||||
end.
|
||||
|
||||
get_module_opt(Module, Opt, Default) ->
|
||||
OptsList = ets:lookup(ejabberd_modules, Module),
|
||||
case OptsList of
|
||||
[] ->
|
||||
Default;
|
||||
[#ejabberd_module{opts = Opts} | _] ->
|
||||
get_opt(Opt, Opts, Default)
|
||||
end.
|
||||
|
||||
loaded_modules() ->
|
||||
ets:select(ejabberd_modules,
|
||||
[{#ejabberd_module{_ = '_', module = '$1'}, [],['$1']}]).
|
||||
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : idna.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Support for IDNA (RFC3490)
|
||||
%%% Created : 10 Apr 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(idna).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%-compile(export_all).
|
||||
-export([domain_utf8_to_ascii/1,
|
||||
domain_ucs2_to_ascii/1]).
|
||||
|
||||
|
||||
domain_utf8_to_ascii(Domain) ->
|
||||
domain_ucs2_to_ascii(utf8_to_ucs2(Domain)).
|
||||
|
||||
utf8_to_ucs2(S) ->
|
||||
utf8_to_ucs2(S, "").
|
||||
|
||||
utf8_to_ucs2([], R) ->
|
||||
lists:reverse(R);
|
||||
utf8_to_ucs2([C | S], R) when C < 16#80 ->
|
||||
utf8_to_ucs2(S, [C | R]);
|
||||
utf8_to_ucs2([C1, C2 | S], R) when C1 < 16#E0 ->
|
||||
utf8_to_ucs2(S, [((C1 band 16#1F) bsl 6) bor
|
||||
(C2 band 16#3F) | R]);
|
||||
utf8_to_ucs2([C1, C2, C3 | S], R) when C1 < 16#F0 ->
|
||||
utf8_to_ucs2(S, [((C1 band 16#0F) bsl 12) bor
|
||||
((C2 band 16#3F) bsl 6) bor
|
||||
(C3 band 16#3F) | R]).
|
||||
|
||||
|
||||
domain_ucs2_to_ascii(Domain) ->
|
||||
case catch domain_ucs2_to_ascii1(Domain) of
|
||||
{'EXIT', _Reason} ->
|
||||
false;
|
||||
Res ->
|
||||
Res
|
||||
end.
|
||||
|
||||
domain_ucs2_to_ascii1(Domain) ->
|
||||
Parts = string:tokens(Domain, [16#002E, 16#3002, 16#FF0E, 16#FF61]),
|
||||
ASCIIParts = lists:map(fun(P) ->
|
||||
to_ascii(P)
|
||||
end, Parts),
|
||||
string:strip(lists:flatmap(fun(P) -> [$. | P] end, ASCIIParts),
|
||||
left, $.).
|
||||
|
||||
% Domain names are already nameprep'ed in ejabberd, so we skiping this step
|
||||
to_ascii(Name) ->
|
||||
false = lists:any(
|
||||
fun(C) when
|
||||
( 0 =< C) and (C =< 16#2C) or
|
||||
(16#2E =< C) and (C =< 16#2F) or
|
||||
(16#3A =< C) and (C =< 16#40) or
|
||||
(16#5B =< C) and (C =< 16#60) or
|
||||
(16#7B =< C) and (C =< 16#7F) ->
|
||||
true;
|
||||
(_) ->
|
||||
false
|
||||
end, Name),
|
||||
case Name of
|
||||
[H | _] when H /= $- ->
|
||||
true = lists:last(Name) /= $-
|
||||
end,
|
||||
ASCIIName = case lists:any(fun(C) -> C > 16#7F end, Name) of
|
||||
true ->
|
||||
true = case Name of
|
||||
"xn--" ++ _ -> false;
|
||||
_ -> true
|
||||
end,
|
||||
"xn--" ++ punycode_encode(Name);
|
||||
false ->
|
||||
Name
|
||||
end,
|
||||
L = length(ASCIIName),
|
||||
true = (1 =< L) and (L =< 63),
|
||||
ASCIIName.
|
||||
|
||||
|
||||
%%% PUNYCODE (RFC3492)
|
||||
|
||||
-define(BASE, 36).
|
||||
-define(TMIN, 1).
|
||||
-define(TMAX, 26).
|
||||
-define(SKEW, 38).
|
||||
-define(DAMP, 700).
|
||||
-define(INITIAL_BIAS, 72).
|
||||
-define(INITIAL_N, 128).
|
||||
|
||||
punycode_encode(Input) ->
|
||||
N = ?INITIAL_N,
|
||||
Delta = 0,
|
||||
Bias = ?INITIAL_BIAS,
|
||||
Basic = lists:filter(fun(C) -> C =< 16#7f end, Input),
|
||||
NonBasic = lists:filter(fun(C) -> C > 16#7f end, Input),
|
||||
L = length(Input),
|
||||
B = length(Basic),
|
||||
SNonBasic = lists:usort(NonBasic),
|
||||
Output1 = if
|
||||
B > 0 -> Basic ++ "-";
|
||||
true -> ""
|
||||
end,
|
||||
Output2 = punycode_encode1(Input, SNonBasic, B, B, L, N, Delta, Bias, ""),
|
||||
Output1 ++ Output2.
|
||||
|
||||
|
||||
punycode_encode1(Input, [M | SNonBasic], B, H, L, N, Delta, Bias, Out)
|
||||
when H < L ->
|
||||
Delta1 = Delta + (M - N) * (H + 1),
|
||||
% let n = m
|
||||
{NewDelta, NewBias, NewH, NewOut} =
|
||||
lists:foldl(
|
||||
fun(C, {ADelta, ABias, AH, AOut}) ->
|
||||
if
|
||||
C < M ->
|
||||
{ADelta + 1, ABias, AH, AOut};
|
||||
C == M ->
|
||||
NewOut = punycode_encode_delta(ADelta, ABias, AOut),
|
||||
NewBias = adapt(ADelta, H + 1, H == B),
|
||||
{0, NewBias, AH + 1, NewOut};
|
||||
true ->
|
||||
{ADelta, ABias, AH, AOut}
|
||||
end
|
||||
end, {Delta1, Bias, H, Out}, Input),
|
||||
punycode_encode1(
|
||||
Input, SNonBasic, B, NewH, L, M + 1, NewDelta + 1, NewBias, NewOut);
|
||||
|
||||
punycode_encode1(Input, SNonBasic, B, H, L, N, Delta, Bias, Out) ->
|
||||
lists:reverse(Out).
|
||||
|
||||
|
||||
punycode_encode_delta(Delta, Bias, Out) ->
|
||||
punycode_encode_delta(Delta, Bias, Out, ?BASE).
|
||||
|
||||
punycode_encode_delta(Delta, Bias, Out, K) ->
|
||||
T = if
|
||||
K =< Bias -> ?TMIN;
|
||||
K >= Bias + ?TMAX -> ?TMAX;
|
||||
true -> K - Bias
|
||||
end,
|
||||
if
|
||||
Delta < T ->
|
||||
[codepoint(Delta) | Out];
|
||||
true ->
|
||||
C = T + ((Delta - T) rem (?BASE - T)),
|
||||
punycode_encode_delta((Delta - T) div (?BASE - T), Bias,
|
||||
[codepoint(C) | Out], K + ?BASE)
|
||||
end.
|
||||
|
||||
|
||||
adapt(Delta, NumPoints, FirstTime) ->
|
||||
Delta1 = if
|
||||
FirstTime -> Delta div ?DAMP;
|
||||
true -> Delta div 2
|
||||
end,
|
||||
Delta2 = Delta1 + (Delta1 div NumPoints),
|
||||
adapt1(Delta2, 0).
|
||||
|
||||
adapt1(Delta, K) ->
|
||||
if
|
||||
Delta > ((?BASE - ?TMIN) * ?TMAX) div 2 ->
|
||||
adapt1(Delta div (?BASE - ?TMIN), K + ?BASE);
|
||||
true ->
|
||||
K + (((?BASE - ?TMIN + 1) * Delta) div (Delta + ?SKEW))
|
||||
end.
|
||||
|
||||
|
||||
codepoint(C) ->
|
||||
if
|
||||
(0 =< C) and (C =< 25) ->
|
||||
C + 97;
|
||||
(26 =< C) and (C =< 35) ->
|
||||
C + 22
|
||||
end.
|
||||
+28
-26
@@ -82,7 +82,7 @@ init([File, User, Pid]) ->
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
wait_for_xdb({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
wait_for_xdb({xmlstreamstart, Name, _Attrs}, StateData) ->
|
||||
case Name of
|
||||
"xdb" ->
|
||||
{next_state, xdb_data, StateData};
|
||||
@@ -95,7 +95,7 @@ wait_for_xdb(closed, StateData) ->
|
||||
|
||||
|
||||
xdb_data({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, Name, Attrs, Els} = El,
|
||||
{xmlelement, _Name, Attrs, _Els} = El,
|
||||
Server = StateData#state.server,
|
||||
From = jlib:make_jid(StateData#state.user, Server, ""),
|
||||
NewState =
|
||||
@@ -105,37 +105,39 @@ xdb_data({xmlstreamelement, El}, StateData) ->
|
||||
ejabberd_auth:set_password(StateData#state.user, Password),
|
||||
StateData;
|
||||
?NS_ROSTER ->
|
||||
%mod_roster:process_iq(From,
|
||||
% {"", ?MYNAME, ""},
|
||||
% {iq, "", set, ?NS_ROSTER, El}),
|
||||
mod_roster:set_items(StateData#state.user, El),
|
||||
%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 ->
|
||||
Res = mod_vcard:process_sm_iq(From,
|
||||
jlib:make_jid("", ?MYNAME, ""),
|
||||
{iq, "", set, ?NS_VCARD, El}),
|
||||
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 ->
|
||||
% mod_register:process_iq(
|
||||
% {"", "", ""}, {"", ?MYNAME, ""},
|
||||
% {iq, "", set, ?NS_REGISTER, El}),
|
||||
% 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" ->
|
||||
mod_private:process_local_iq(
|
||||
From,
|
||||
jlib:make_jid("", ?MYNAME, ""),
|
||||
{iq, "", set, ?NS_PRIVATE,
|
||||
{xmlelement, "query", [],
|
||||
[jlib:remove_attr(
|
||||
"j_private_flag",
|
||||
jlib:remove_attr("xdbns", El))]}}),
|
||||
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",
|
||||
@@ -145,7 +147,7 @@ xdb_data({xmlstreamelement, El}, StateData) ->
|
||||
end,
|
||||
{next_state, xdb_data, NewState};
|
||||
|
||||
xdb_data({xmlstreamend, Name}, StateData) ->
|
||||
xdb_data({xmlstreamend, _Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
xdb_data(closed, StateData) ->
|
||||
@@ -172,7 +174,7 @@ xdb_data(closed, StateData) ->
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(Event, StateName, StateData) ->
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
@@ -184,11 +186,11 @@ handle_event(Event, StateName, StateData) ->
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event(Event, From, StateName, StateData) ->
|
||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData}.
|
||||
|
||||
code_change(OldVsn, StateName, StateData, Extra) ->
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
@@ -205,7 +207,7 @@ handle_info(_, StateName, StateData) ->
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StateData) ->
|
||||
terminate(Reason, _StateName, StateData) ->
|
||||
exit(StateData#state.xml_stream_pid, closed),
|
||||
StateData#state.pid ! {jd2ejd, Reason},
|
||||
% Profiling
|
||||
@@ -231,7 +233,7 @@ process_offline(To, {xmlelement, _, _, Els}) ->
|
||||
error ->
|
||||
ok;
|
||||
_ ->
|
||||
mod_offline:store_packet(From, To, El)
|
||||
catch mod_offline:store_packet(From, To, El)
|
||||
end
|
||||
end, Els).
|
||||
|
||||
|
||||
+127
-2
@@ -37,6 +37,9 @@
|
||||
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]).
|
||||
|
||||
@@ -329,6 +332,7 @@ get_iq_namespace(_) ->
|
||||
iq_query_info({xmlelement, Name, Attrs, Els}) when Name == "iq" ->
|
||||
ID = xml:get_attr_s("id", Attrs),
|
||||
Type = xml:get_attr_s("type", Attrs),
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
Type1 = case Type of
|
||||
"set" -> set;
|
||||
"get" -> get;
|
||||
@@ -343,7 +347,11 @@ iq_query_info({xmlelement, Name, Attrs, Els}) when Name == "iq" ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs2),
|
||||
if
|
||||
XMLNS /= "" ->
|
||||
{iq, ID, Type1, XMLNS, {xmlelement, Name2, Attrs2, Els2}};
|
||||
#iq{id = ID,
|
||||
type = Type1,
|
||||
xmlns = XMLNS,
|
||||
lang = Lang,
|
||||
sub_el = {xmlelement, Name2, Attrs2, Els2}};
|
||||
true ->
|
||||
invalid
|
||||
end;
|
||||
@@ -367,7 +375,7 @@ iq_type_to_string(error) -> "error";
|
||||
iq_type_to_string(_) -> invalid.
|
||||
|
||||
|
||||
iq_to_xml({iq, ID, Type, _, SubEl}) ->
|
||||
iq_to_xml(#iq{id = ID, type = Type, sub_el = SubEl}) ->
|
||||
if
|
||||
ID /= "" ->
|
||||
{xmlelement, "iq",
|
||||
@@ -433,6 +441,123 @@ 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)
|
||||
|
||||
+113
-32
@@ -21,6 +21,7 @@
|
||||
-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").
|
||||
@@ -33,15 +34,21 @@
|
||||
-define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
|
||||
-define(NS_PUBSUB_NMI, "http://jabber.org/protocol/pubsub#node-meta-info").
|
||||
|
||||
-define(NS_EJABBERD_CONFIG, "ejabberd:config").
|
||||
|
||||
-define(NS_STREAM, "http://etherx.jabber.org/streams").
|
||||
|
||||
-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),
|
||||
{xmlelement, "error",
|
||||
@@ -49,62 +56,122 @@
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}).
|
||||
|
||||
-define(ERR_BAD_REQUEST,
|
||||
?STANZA_ERROR("400", "modify", "bad-request")).
|
||||
?STANZA_ERROR("400", "modify", "bad-request")).
|
||||
-define(ERR_CONFLICT,
|
||||
?STANZA_ERROR("409", "cancel", "conflict")).
|
||||
?STANZA_ERROR("409", "cancel", "conflict")).
|
||||
-define(ERR_FEATURE_NOT_IMPLEMENTED,
|
||||
?STANZA_ERROR("501", "cancel", "feature-not-implemented")).
|
||||
?STANZA_ERROR("501", "cancel", "feature-not-implemented")).
|
||||
-define(ERR_FORBIDDEN,
|
||||
?STANZA_ERROR("403", "auth", "forbidden")).
|
||||
?STANZA_ERROR("403", "auth", "forbidden")).
|
||||
-define(ERR_GONE,
|
||||
?STANZA_ERROR("302", "modify", "gone")).
|
||||
-define(ERR_INTERNAL_SERVER_ERROR,
|
||||
?STANZA_ERROR("500", "wait", "internal-server-error")).
|
||||
?STANZA_ERROR("500", "wait", "internal-server-error")).
|
||||
-define(ERR_ITEM_NOT_FOUND,
|
||||
?STANZA_ERROR("404", "cancel", "item-not-found")).
|
||||
?STANZA_ERROR("404", "cancel", "item-not-found")).
|
||||
-define(ERR_JID_MALFORMED,
|
||||
?STANZA_ERROR("400", "modify", "jid-malformed")).
|
||||
?STANZA_ERROR("400", "modify", "jid-malformed")).
|
||||
-define(ERR_NOT_ACCEPTABLE,
|
||||
?STANZA_ERROR("406", "modify", "not-acceptable")).
|
||||
-define(ERR_NOT_ALLOWED,
|
||||
?STANZA_ERROR("405", "cancel", "not-allowed")).
|
||||
?STANZA_ERROR("405", "cancel", "not-allowed")).
|
||||
-define(ERR_NOT_AUTHORIZED,
|
||||
?STANZA_ERROR("401", "auth", "not-authorized")).
|
||||
-define(ERR_PAYMENT_REQUIRED,
|
||||
?STANZA_ERROR("402", "auth", "payment-required")).
|
||||
-define(ERR_RECIPIENT_UNAVAILABLE,
|
||||
?STANZA_ERROR("404", "wait", "recipient-unavailable")).
|
||||
?STANZA_ERROR("404", "wait", "recipient-unavailable")).
|
||||
-define(ERR_REDIRECT,
|
||||
?STANZA_ERROR("302", "modify", "redirect")).
|
||||
-define(ERR_REGISTRATION_REQUIRED,
|
||||
?STANZA_ERROR("407", "auth", "registration-required")).
|
||||
?STANZA_ERROR("407", "auth", "registration-required")).
|
||||
-define(ERR_REMOTE_SERVER_NOT_FOUND,
|
||||
?STANZA_ERROR("404", "cancel", "remote-server-not-found")).
|
||||
?STANZA_ERROR("404", "cancel", "remote-server-not-found")).
|
||||
-define(ERR_REMOTE_SERVER_TIMEOUT,
|
||||
?STANZA_ERROR("504", "wait", "remote-server-timeout")).
|
||||
?STANZA_ERROR("504", "wait", "remote-server-timeout")).
|
||||
-define(ERR_RESOURCE_CONSTRAINT,
|
||||
?STANZA_ERROR("0", "wait", "resource-constraint")).
|
||||
?STANZA_ERROR("500", "wait", "resource-constraint")).
|
||||
-define(ERR_SERVICE_UNAVAILABLE,
|
||||
?STANZA_ERROR("503", "cancel", "service-unavailable")).
|
||||
?STANZA_ERROR("503", "cancel", "service-unavailable")).
|
||||
-define(ERR_SUBSCRIPTION_REQUIRED,
|
||||
?STANZA_ERROR("0", "auth", "subscription-required")).
|
||||
?STANZA_ERROR("407", "auth", "subscription-required")).
|
||||
-define(ERR_UNEXPECTED_REQUEST,
|
||||
?STANZA_ERROR("0", "wait", "unexpected-request")).
|
||||
?STANZA_ERROR("400", "wait", "unexpected-request")).
|
||||
%-define(ERR_,
|
||||
% ?STANZA_ERROR("", "", "")).
|
||||
|
||||
% TODO: update to new-style
|
||||
% Application-specific stanza errors
|
||||
-define(AUTH_STANZA_ERROR(Condition),
|
||||
-define(STANZA_ERRORT(Code, Type, Condition, Lang, Text),
|
||||
{xmlelement, "error",
|
||||
[{"code", "406"}, {"class", "app"}],
|
||||
[{xmlelement, "auth-condition",
|
||||
[{"xmlns", ?NS_AUTH_ERROR}],
|
||||
[{xmlelement, Condition, [], []}]}]}).
|
||||
[{"code", Code}, {"type", Type}],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []},
|
||||
{xmlelement, "text", [{"xmlns", ?NS_STANZAS}],
|
||||
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
||||
|
||||
-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(ERRT_BAD_REQUEST(Lang, Text),
|
||||
?STANZA_ERRORT("400", "modify", "bad-request", Lang, Text)).
|
||||
-define(ERRT_CONFLICT(Lang, Text),
|
||||
?STANZA_ERRORT("409", "cancel", "conflict", Lang, Text)).
|
||||
-define(ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Text),
|
||||
?STANZA_ERRORT("501", "cancel", "feature-not-implemented", Lang, Text)).
|
||||
-define(ERRT_FORBIDDEN(Lang, Text),
|
||||
?STANZA_ERRORT("403", "auth", "forbidden", Lang, Text)).
|
||||
-define(ERRT_GONE(Lang, Text),
|
||||
?STANZA_ERRORT("302", "modify", "gone", Lang, Text)).
|
||||
-define(ERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||
?STANZA_ERRORT("500", "wait", "internal-server-error", Lang, Text)).
|
||||
-define(ERRT_ITEM_NOT_FOUND(Lang, Text),
|
||||
?STANZA_ERRORT("404", "cancel", "item-not-found", Lang, Text)).
|
||||
-define(ERRT_JID_MALFORMED(Lang, Text),
|
||||
?STANZA_ERRORT("400", "modify", "jid-malformed", Lang, Text)).
|
||||
-define(ERRT_NOT_ACCEPTABLE(Lang, Text),
|
||||
?STANZA_ERRORT("406", "modify", "not-acceptable", Lang, Text)).
|
||||
-define(ERRT_NOT_ALLOWED(Lang, Text),
|
||||
?STANZA_ERRORT("405", "cancel", "not-allowed", Lang, Text)).
|
||||
-define(ERRT_NOT_AUTHORIZED(Lang, Text),
|
||||
?STANZA_ERRORT("401", "auth", "not-authorized", Lang, Text)).
|
||||
-define(ERRT_PAYMENT_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT("402", "auth", "payment-required", Lang, Text)).
|
||||
-define(ERRT_RECIPIENT_UNAVAILABLE(Lang, Text),
|
||||
?STANZA_ERRORT("404", "wait", "recipient-unavailable", Lang, Text)).
|
||||
-define(ERRT_REDIRECT(Lang, Text),
|
||||
?STANZA_ERRORT("302", "modify", "redirect", Lang, Text)).
|
||||
-define(ERRT_REGISTRATION_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT("407", "auth", "registration-required", Lang, Text)).
|
||||
-define(ERRT_REMOTE_SERVER_NOT_FOUND(Lang, Text),
|
||||
?STANZA_ERRORT("404", "cancel", "remote-server-not-found", Lang, Text)).
|
||||
-define(ERRT_REMOTE_SERVER_TIMEOUT(Lang, Text),
|
||||
?STANZA_ERRORT("504", "wait", "remote-server-timeout", Lang, Text)).
|
||||
-define(ERRT_RESOURCE_CONSTRAINT(Lang, Text),
|
||||
?STANZA_ERRORT("500", "wait", "resource-constraint", Lang, Text)).
|
||||
-define(ERRT_SERVICE_UNAVAILABLE(Lang, Text),
|
||||
?STANZA_ERRORT("503", "cancel", "service-unavailable", Lang, Text)).
|
||||
-define(ERRT_SUBSCRIPTION_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT("407", "auth", "subscription-required", Lang, Text)).
|
||||
-define(ERRT_UNEXPECTED_REQUEST(Lang, Text),
|
||||
?STANZA_ERRORT("400", "wait", "unexpected-request", Lang, Text)).
|
||||
|
||||
% 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")).
|
||||
|
||||
|
||||
-define(STREAM_ERROR(Condition),
|
||||
{xmlelement, "stream:error",
|
||||
[],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}).
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}]}).
|
||||
|
||||
-define(SERR_BAD_FORMAT,
|
||||
?STREAM_ERROR("bad-format")).
|
||||
-define(SERR_BAD_NAMESPACE_PREFIX,
|
||||
?STREAM_ERROR("bad-namespace-prefix")).
|
||||
-define(SERR_CONFLICT,
|
||||
?STREAM_ERROR("conflict")).
|
||||
-define(SERR_CONNECTION_TIMEOUT,
|
||||
?STREAM_ERROR("connection-timeout")).
|
||||
-define(SERR_HOST_GONE,
|
||||
?STREAM_ERROR("host-gone")).
|
||||
-define(SERR_HOST_UNKNOWN,
|
||||
@@ -113,23 +180,31 @@
|
||||
?STREAM_ERROR("improper-addressing")).
|
||||
-define(SERR_INTERNAL_SERVER_ERROR,
|
||||
?STREAM_ERROR("internal-server-error")).
|
||||
-define(SERR_INVALID_FROM,
|
||||
?STREAM_ERROR("invalid-from")).
|
||||
-define(SERR_INVALID_ID,
|
||||
?STREAM_ERROR("invalid-id")).
|
||||
-define(SERR_INVALID_NAMESPACE,
|
||||
?STREAM_ERROR("invalid-namespace")).
|
||||
-define(SERR_NONMATCHING_HOSTS,
|
||||
?STREAM_ERROR("nonmatching-hosts")).
|
||||
-define(SERR_INVALID_XML,
|
||||
?STREAM_ERROR("invalid-xml")).
|
||||
-define(SERR_NOT_AUTHORIZED,
|
||||
?STREAM_ERROR("not-authorized")).
|
||||
-define(SERR_POLICY_VIOLATION,
|
||||
?STREAM_ERROR("policy-violation")).
|
||||
-define(SERR_REMOTE_CONNECTION_FAILED,
|
||||
?STREAM_ERROR("remote-connection-failed")).
|
||||
-define(SERR_RESOURSE_CONSTRAINT,
|
||||
?STREAM_ERROR("resource-constraint")).
|
||||
-define(SERR_RESTRICTED_XML,
|
||||
?STREAM_ERROR("restricted-xml")).
|
||||
% TODO: include hostname or IP
|
||||
-define(SERR_SEE_OTHER_HOST,
|
||||
?STREAM_ERROR("see-other-host")).
|
||||
-define(SERR_SYSTEM_SHUTDOWN,
|
||||
?STREAM_ERROR("system-shutdown")).
|
||||
-define(SERR_UNSUPPORTED_ENCODING,
|
||||
?STREAM_ERROR("unsupported-encoding")).
|
||||
-define(SERR_UNSUPPORTED_STANZA_TYPE,
|
||||
?STREAM_ERROR("unsupported-stanza-type")).
|
||||
-define(SERR_UNSUPPORTED_VERSION,
|
||||
@@ -143,3 +218,9 @@
|
||||
-record(jid, {user, server, resource,
|
||||
luser, lserver, lresource}).
|
||||
|
||||
-record(iq, {id = "",
|
||||
type,
|
||||
xmlns = "",
|
||||
lang = "",
|
||||
sub_el}).
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% 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, {id, packet}).
|
||||
-record(motd_users, {luser, 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)}]),
|
||||
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_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),
|
||||
ok.
|
||||
|
||||
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/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 ->
|
||||
Server = ?MYNAME,
|
||||
Local = jlib:make_jid("", Server, ""),
|
||||
lists:foreach(
|
||||
fun(U) ->
|
||||
Dest = jlib:make_jid(U, Server, ""),
|
||||
ejabberd_router:route(Local, Dest, Packet)
|
||||
end, ejabberd_auth:dirty_get_registered_users())
|
||||
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:dirty_get_sessions_list(), Packet)
|
||||
end.
|
||||
|
||||
announce_online1(Sessions, Packet) ->
|
||||
Server = ?MYNAME,
|
||||
Local = jlib:make_jid("", Server, ""),
|
||||
lists:foreach(
|
||||
fun({U, R}) ->
|
||||
Dest = jlib:make_jid(U, Server, 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(Packet),
|
||||
Sessions = ejabberd_sm:dirty_get_sessions_list(),
|
||||
announce_online1(Sessions, Packet),
|
||||
F = fun() ->
|
||||
lists:foreach(
|
||||
fun({U, _R}) ->
|
||||
mnesia:write(#motd_users{luser = U})
|
||||
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(Packet)
|
||||
end.
|
||||
|
||||
announce_motd_update(Packet) ->
|
||||
announce_motd_delete(),
|
||||
F = fun() ->
|
||||
mnesia:write(#motd{id = motd, 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()
|
||||
end.
|
||||
|
||||
announce_motd_delete() ->
|
||||
mnesia:clear_table(motd),
|
||||
mnesia:clear_table(motd_users).
|
||||
|
||||
send_motd(#jid{luser = LUser} = JID) ->
|
||||
case catch mnesia:dirty_read({motd, motd}) of
|
||||
[#motd{packet = Packet}] ->
|
||||
case catch mnesia:dirty_read({motd_users, LUser}) of
|
||||
[#motd_users{}] ->
|
||||
ok;
|
||||
_ ->
|
||||
Local = jlib:make_jid("", ?MYNAME, ""),
|
||||
ejabberd_router:route(Local, JID, Packet),
|
||||
F = fun() ->
|
||||
mnesia:write(#motd_users{luser = LUser})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
+372
-322
@@ -23,68 +23,77 @@
|
||||
|
||||
start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_IQDATA,
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_EJABBERD_CONFIG,
|
||||
?MODULE, process_local_iq, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_IQDATA,
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_EJABBERD_CONFIG,
|
||||
?MODULE, process_sm_iq, IQDisc),
|
||||
ok.
|
||||
|
||||
stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_IQDATA),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_IQDATA).
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_EJABBERD_CONFIG),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_EJABBERD_CONFIG).
|
||||
|
||||
|
||||
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_local_iq(From, _To, #iq{id = ID, type = Type, xmlns = XMLNS,
|
||||
lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case acl:match_rule(configure, From) of
|
||||
deny ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
allow ->
|
||||
Lang = xml:get_tag_attr_s("xml:lang", SubEl),
|
||||
case Type of
|
||||
set ->
|
||||
case xml:get_tag_attr_s("type", SubEl) of
|
||||
"cancel" ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query", [{"xmlns", XMLNS}], []}]};
|
||||
"submit" ->
|
||||
XData = jlib:parse_xdata_submit(SubEl),
|
||||
case XData of
|
||||
invalid ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_BAD_REQUEST]};
|
||||
XDataEl = find_xdata_el(SubEl),
|
||||
case XDataEl of
|
||||
false ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
|
||||
{xmlelement, _Name, Attrs, SubEls} ->
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"cancel" ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}], []}]};
|
||||
"submit" ->
|
||||
XData = jlib:parse_xdata_submit(XDataEl),
|
||||
case XData of
|
||||
invalid ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_BAD_REQUEST]};
|
||||
_ ->
|
||||
Node =
|
||||
string:tokens(
|
||||
xml:get_tag_attr_s("node", SubEl),
|
||||
"/"),
|
||||
case set_form(Node, Lang, XData) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el =
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
{error, Error} ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, Error]}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
Node =
|
||||
string:tokens(
|
||||
xml:get_tag_attr_s("node", SubEl),
|
||||
"/"),
|
||||
case set_form(Node, Lang, XData) of
|
||||
{result, Res} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, Error]}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_BAD_REQUEST]}
|
||||
end
|
||||
end;
|
||||
get ->
|
||||
Node =
|
||||
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||
case get_form(Node, Lang) of
|
||||
{result, Res} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query", [{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el =
|
||||
[{xmlelement, "query", [{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, Error]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, Error]}
|
||||
end
|
||||
end
|
||||
end.
|
||||
@@ -129,31 +138,33 @@ get_form(["running nodes", ENode, "DB"], Lang) ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
Node ->
|
||||
case rpc:call(Node, mnesia, system_info, [tables]) of
|
||||
{badrpc, Reason} ->
|
||||
{badrpc, _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
Tables ->
|
||||
STables = lists:sort(Tables),
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "DB Tables Configuration")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Choose storage type of tables")}]} |
|
||||
lists:map(
|
||||
fun(Table) ->
|
||||
case rpc:call(Node,
|
||||
mnesia,
|
||||
table_info,
|
||||
[Table, storage_type]) of
|
||||
{badrpc, _} ->
|
||||
?TABLEFIELD(Table, unknown);
|
||||
Type ->
|
||||
?TABLEFIELD(Table, Type)
|
||||
end
|
||||
end, STables)
|
||||
]}
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "DB Tables Configuration at ") ++
|
||||
ENode}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Choose storage type of tables")}]} |
|
||||
lists:map(
|
||||
fun(Table) ->
|
||||
case rpc:call(Node,
|
||||
mnesia,
|
||||
table_info,
|
||||
[Table, storage_type]) of
|
||||
{badrpc, _} ->
|
||||
?TABLEFIELD(Table, unknown);
|
||||
Type ->
|
||||
?TABLEFIELD(Table, Type)
|
||||
end
|
||||
end, STables)
|
||||
]}]}
|
||||
end
|
||||
end;
|
||||
|
||||
@@ -163,221 +174,234 @@ get_form(["running nodes", ENode, "modules", "stop"], Lang) ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
Node ->
|
||||
case rpc:call(Node, gen_mod, loaded_modules, []) of
|
||||
{badrpc, Reason} ->
|
||||
{badrpc, _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
Modules ->
|
||||
SModules = lists:sort(Modules),
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Stop Modules")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Choose modules to stop")}]} |
|
||||
lists:map(fun(M) ->
|
||||
S = atom_to_list(M),
|
||||
?XFIELD("boolean", S, S, "0")
|
||||
end, SModules)
|
||||
]}
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Stop Modules at ") ++ ENode}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Choose modules to stop")}]} |
|
||||
lists:map(fun(M) ->
|
||||
S = atom_to_list(M),
|
||||
?XFIELD("boolean", S, S, "0")
|
||||
end, SModules)
|
||||
]}]}
|
||||
end
|
||||
end;
|
||||
|
||||
get_form(["running nodes", ENode, "modules", "start"], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Start Modules")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter list of {Module, [Options]}")}]},
|
||||
{xmlelement, "field", [{"type", "text-multi"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "List of modules to start")},
|
||||
{"var", "modules"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, "[]."}]}]
|
||||
}
|
||||
]};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Start Modules at ") ++ ENode}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter list of {Module, [Options]}")}]},
|
||||
{xmlelement, "field", [{"type", "text-multi"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "List of modules to start")},
|
||||
{"var", "modules"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, "[]."}]}]
|
||||
}
|
||||
]}]};
|
||||
|
||||
get_form(["running nodes", ENode, "backup", "backup"], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Backup to File")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter path to backup file")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Path to File")},
|
||||
{"var", "path"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ""}]}]
|
||||
}
|
||||
]};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Backup to File at ") ++ ENode}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter path to backup file")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Path to File")},
|
||||
{"var", "path"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ""}]}]
|
||||
}
|
||||
]}]};
|
||||
|
||||
get_form(["running nodes", ENode, "backup", "restore"], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Restore Backup from File")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter path to backup file")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Path to File")},
|
||||
{"var", "path"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ""}]}]
|
||||
}
|
||||
]};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Restore Backup from File at ") ++ ENode}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter path to backup file")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Path to File")},
|
||||
{"var", "path"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ""}]}]
|
||||
}
|
||||
]}]};
|
||||
|
||||
get_form(["running nodes", ENode, "backup", "textfile"], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Dump Backup to Text File")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter path to text file")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Path to File")},
|
||||
{"var", "path"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ""}]}]
|
||||
}
|
||||
]};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Dump Backup to Text File at ") ++ ENode}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter path to text file")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Path to File")},
|
||||
{"var", "path"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ""}]}]
|
||||
}
|
||||
]}]};
|
||||
|
||||
get_form(["running nodes", ENode, "import", "file"], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Import User from File")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter path to jabberd1.4 spool file")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Path to File")},
|
||||
{"var", "path"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ""}]}]
|
||||
}
|
||||
]};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Import User from File at ") ++ ENode}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter path to jabberd1.4 spool file")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Path to File")},
|
||||
{"var", "path"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ""}]}]
|
||||
}
|
||||
]}]};
|
||||
|
||||
get_form(["running nodes", ENode, "import", "dir"], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Import User from Dir")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter path to jabberd1.4 spool dir")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Path to Dir")},
|
||||
{"var", "path"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ""}]}]
|
||||
}
|
||||
]};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Import Users from Dir at ") ++ ENode}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter path to jabberd1.4 spool dir")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Path to Dir")},
|
||||
{"var", "path"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ""}]}]
|
||||
}
|
||||
]}]};
|
||||
|
||||
get_form(["config", "hostname"], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Hostname Configuration")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Choose host name")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(Lang, "Host name")},
|
||||
{"var", "hostname"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
|
||||
]};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Hostname Configuration")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Choose host name")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(Lang,
|
||||
"Host name")},
|
||||
{"var", "hostname"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
|
||||
]}]};
|
||||
|
||||
get_form(["config", "acls"], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "ACLs Configuration")}]},
|
||||
%{xmlelement, "instructions", [],
|
||||
% [{xmlcdata,
|
||||
% translate:translate(
|
||||
% Lang, "")}]},
|
||||
{xmlelement, "field", [{"type", "text-multi"},
|
||||
{"label",
|
||||
translate:translate(Lang, "ACLs")},
|
||||
{"var", "acls"}],
|
||||
lists:map(fun(S) ->
|
||||
{xmlelement, "value", [], [{xmlcdata, S}]}
|
||||
end,
|
||||
string:tokens(
|
||||
lists:flatten(io_lib:format("~p.",
|
||||
[ets:tab2list(acl)])),
|
||||
"\n"))
|
||||
}
|
||||
]};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Access Control List Configuration")}]},
|
||||
%{xmlelement, "instructions", [],
|
||||
% [{xmlcdata,
|
||||
% translate:translate(
|
||||
% Lang, "")}]},
|
||||
{xmlelement, "field", [{"type", "text-multi"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Access control lists")},
|
||||
{"var", "acls"}],
|
||||
lists:map(fun(S) ->
|
||||
{xmlelement, "value", [], [{xmlcdata, S}]}
|
||||
end,
|
||||
string:tokens(
|
||||
lists:flatten(io_lib:format("~p.",
|
||||
[ets:tab2list(acl)])),
|
||||
"\n"))
|
||||
}
|
||||
]}]};
|
||||
|
||||
get_form(["config", "access"], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Access Configuration")}]},
|
||||
%{xmlelement, "instructions", [],
|
||||
% [{xmlcdata,
|
||||
% translate:translate(
|
||||
% Lang, "")}]},
|
||||
{xmlelement, "field", [{"type", "text-multi"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Access Rules")},
|
||||
{"var", "access"}],
|
||||
lists:map(fun(S) ->
|
||||
{xmlelement, "value", [], [{xmlcdata, S}]}
|
||||
end,
|
||||
string:tokens(
|
||||
lists:flatten(
|
||||
io_lib:format(
|
||||
"~p.",
|
||||
[ets:select(config,
|
||||
[{{config, {access, '$1'}, '$2'},
|
||||
[],
|
||||
[{{access, '$1', '$2'}}]}])
|
||||
])),
|
||||
"\n"))
|
||||
}
|
||||
]};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Access Configuration")}]},
|
||||
%{xmlelement, "instructions", [],
|
||||
% [{xmlcdata,
|
||||
% translate:translate(
|
||||
% Lang, "")}]},
|
||||
{xmlelement, "field", [{"type", "text-multi"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "Access rules")},
|
||||
{"var", "access"}],
|
||||
lists:map(fun(S) ->
|
||||
{xmlelement, "value", [], [{xmlcdata, S}]}
|
||||
end,
|
||||
string:tokens(
|
||||
lists:flatten(
|
||||
io_lib:format(
|
||||
"~p.",
|
||||
[ets:select(config,
|
||||
[{{config, {access, '$1'}, '$2'},
|
||||
[],
|
||||
[{{access, '$1', '$2'}}]}])
|
||||
])),
|
||||
"\n"))
|
||||
}
|
||||
]}]};
|
||||
|
||||
get_form(["config", "remusers"], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Remove Users")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Choose users to remove")}]}] ++
|
||||
case catch ejabberd_auth:dirty_get_registered_users() of
|
||||
{'EXIT', Reason} ->
|
||||
[];
|
||||
Users ->
|
||||
lists:map(fun(U) ->
|
||||
?XFIELD("boolean", U, U, "0")
|
||||
end, lists:sort(Users))
|
||||
end
|
||||
};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Remove Users")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Choose users to remove")}]}] ++
|
||||
case catch ejabberd_auth:dirty_get_registered_users() of
|
||||
{'EXIT', Reason} ->
|
||||
[];
|
||||
Users ->
|
||||
lists:map(fun(U) ->
|
||||
?XFIELD("boolean", U, U, "0")
|
||||
end, lists:sort(Users))
|
||||
end
|
||||
}]};
|
||||
|
||||
get_form(_, Lang) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
@@ -391,7 +415,7 @@ set_form(["running nodes", ENode, "DB"], Lang, XData) ->
|
||||
Node ->
|
||||
lists:foreach(
|
||||
fun({SVar, SVals}) ->
|
||||
% We believe that this is allowed only for good peoples
|
||||
% We believe that this is allowed only for good people
|
||||
Table = list_to_atom(SVar),
|
||||
Type = case SVals of
|
||||
["unknown"] -> unknown;
|
||||
@@ -698,93 +722,102 @@ search_running_node(SNode, [Node | Nodes]) ->
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
process_sm_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_sm_iq(From, To,
|
||||
#iq{type = Type, xmlns = XMLNS, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case acl:match_rule(configure, From) of
|
||||
deny ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
allow ->
|
||||
#jid{user = User} = To,
|
||||
Lang = xml:get_tag_attr_s("xml:lang", SubEl),
|
||||
case Type of
|
||||
set ->
|
||||
case xml:get_tag_attr_s("type", SubEl) of
|
||||
"cancel" ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query", [{"xmlns", XMLNS}], []}]};
|
||||
"submit" ->
|
||||
XData = jlib:parse_xdata_submit(SubEl),
|
||||
case XData of
|
||||
invalid ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_BAD_REQUEST]};
|
||||
XDataEl = find_xdata_el(SubEl),
|
||||
case XDataEl of
|
||||
false ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
|
||||
{xmlelement, _Name, Attrs, SubEls} ->
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"cancel" ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}], []}]};
|
||||
"submit" ->
|
||||
XData = jlib:parse_xdata_submit(XDataEl),
|
||||
case XData of
|
||||
invalid ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_BAD_REQUEST]};
|
||||
_ ->
|
||||
Node =
|
||||
string:tokens(
|
||||
xml:get_tag_attr_s("node", SubEl),
|
||||
"/"),
|
||||
case set_sm_form(
|
||||
User, Node, Lang, XData) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el =
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
{error, Error} ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, Error]}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
Node =
|
||||
string:tokens(
|
||||
xml:get_tag_attr_s("node", SubEl),
|
||||
"/"),
|
||||
case set_sm_form(
|
||||
User, Node, Lang, XData) of
|
||||
{result, Res} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, Error]}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_BAD_REQUEST]}
|
||||
end
|
||||
end;
|
||||
get ->
|
||||
Node =
|
||||
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||
case get_sm_form(User, Node, Lang) of
|
||||
{result, Res} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query", [{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el =
|
||||
[{xmlelement, "query", [{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, Error]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
get_sm_form(User, [], Lang) ->
|
||||
{result, [{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Administration of " ++ User)}]},
|
||||
%{xmlelement, "instructions", [],
|
||||
% [{xmlcdata,
|
||||
% translate:translate(
|
||||
% Lang, "Choose host name")}]},
|
||||
{xmlelement, "field",
|
||||
[{"type", "list-single"},
|
||||
{"label", translate:translate(Lang, "Action on user")},
|
||||
{"var", "action"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, "edit"}]},
|
||||
{xmlelement, "option",
|
||||
[{"label", translate:translate(Lang, "Edit Properties")}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, "edit"}]}]},
|
||||
{xmlelement, "option",
|
||||
[{"label", translate:translate(Lang, "Remove User")}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, "remove"}]}]}
|
||||
]},
|
||||
?XFIELD("text-private", "Password", "password",
|
||||
ejabberd_auth:get_password_s(User))
|
||||
%{xmlelement, "field", [{"type", "text-single"},
|
||||
% {"label",
|
||||
% translate:translate(Lang, "Host name")},
|
||||
% {"var", "hostname"}],
|
||||
% [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
|
||||
]};
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Administration of ") ++ User}]},
|
||||
%{xmlelement, "instructions", [],
|
||||
% [{xmlcdata,
|
||||
% translate:translate(
|
||||
% Lang, "Choose host name")}]},
|
||||
{xmlelement, "field",
|
||||
[{"type", "list-single"},
|
||||
{"label", translate:translate(Lang, "Action on user")},
|
||||
{"var", "action"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, "edit"}]},
|
||||
{xmlelement, "option",
|
||||
[{"label", translate:translate(Lang, "Edit Properties")}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, "edit"}]}]},
|
||||
{xmlelement, "option",
|
||||
[{"label", translate:translate(Lang, "Remove User")}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, "remove"}]}]}
|
||||
]},
|
||||
?XFIELD("text-private", "Password", "password",
|
||||
ejabberd_auth:get_password_s(User))
|
||||
%{xmlelement, "field", [{"type", "text-single"},
|
||||
% {"label",
|
||||
% translate:translate(Lang, "Host name")},
|
||||
% {"var", "hostname"}],
|
||||
% [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
|
||||
]}]};
|
||||
|
||||
get_sm_form(_, _, Lang) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
@@ -813,3 +846,20 @@ set_sm_form(User, [], Lang, XData) ->
|
||||
end;
|
||||
set_sm_form(_, _, Lang, XData) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
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).
|
||||
|
||||
+22
-23
@@ -28,30 +28,29 @@ start(Opts) ->
|
||||
ok.
|
||||
|
||||
stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_IQDATA),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_IQDATA).
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_ECONFIGURE).
|
||||
|
||||
|
||||
process_local_iq(From, _To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_local_iq(From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case acl:match_rule(configure, From) of
|
||||
deny ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
allow ->
|
||||
%Lang = xml:get_tag_attr_s("xml:lang", SubEl),
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]};
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]};
|
||||
%case xml:get_tag_attr_s("type", SubEl) of
|
||||
% "cancel" ->
|
||||
% {iq, ID, result, XMLNS,
|
||||
% [{xmlelement, "query", [{"xmlns", XMLNS}], []}]};
|
||||
% IQ#iq{type = result,
|
||||
% sub_el = [{xmlelement, "query",
|
||||
% [{"xmlns", XMLNS}], []}]};
|
||||
% "submit" ->
|
||||
% XData = jlib:parse_xdata_submit(SubEl),
|
||||
% case XData of
|
||||
% invalid ->
|
||||
% {iq, ID, error, XMLNS,
|
||||
% [SubEl, ?ERR_BAD_REQUEST]};
|
||||
% IQ#iq{type = error,
|
||||
% sub_el = [SubEl, ?ERR_BAD_REQUEST]};
|
||||
% _ ->
|
||||
% Node =
|
||||
% string:tokens(
|
||||
@@ -59,26 +58,26 @@ process_local_iq(From, _To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
% "/"),
|
||||
% case set_form(Node, Lang, XData) of
|
||||
% {result, Res} ->
|
||||
% {iq, ID, result, XMLNS,
|
||||
% [{xmlelement, "query",
|
||||
% [{"xmlns", XMLNS}],
|
||||
% Res
|
||||
% }]};
|
||||
% IQ#iq{type = result,
|
||||
% sub_el = [{xmlelement, "query",
|
||||
% [{"xmlns", XMLNS}],
|
||||
% Res
|
||||
% }]};
|
||||
% {error, Error} ->
|
||||
% {iq, ID, error, XMLNS,
|
||||
% [SubEl, Error]}
|
||||
% IQ#iq{type = error,
|
||||
% sub_el = [SubEl, Error]}
|
||||
% end
|
||||
% end;
|
||||
% _ ->
|
||||
% {iq, ID, error, XMLNS,
|
||||
% [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
% IQ#iq{type = error,
|
||||
% sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
%end;
|
||||
get ->
|
||||
case process_get(SubEl) of
|
||||
{result, Res} ->
|
||||
{iq, ID, result, XMLNS, [Res]};
|
||||
IQ#iq{type = result, sub_el = [Res]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS, [SubEl, Error]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
end
|
||||
end
|
||||
end.
|
||||
@@ -136,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 ->
|
||||
|
||||
+195
-108
@@ -21,17 +21,24 @@
|
||||
register_feature/1,
|
||||
unregister_feature/1,
|
||||
register_extra_domain/1,
|
||||
unregister_extra_domain/1]).
|
||||
unregister_extra_domain/1,
|
||||
register_sm_feature/1,
|
||||
unregister_sm_feature/1,
|
||||
register_sm_node/4,
|
||||
unregister_sm_node/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(EMPTY_INFO_RESULT,
|
||||
{iq, ID, result, XMLNS, [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_INFO},
|
||||
{"node", SNode}], []}]}).
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_INFO},
|
||||
{"node", SNode}], []}]}).
|
||||
|
||||
start(Opts) ->
|
||||
ejabberd_local:refresh_iq_handlers(),
|
||||
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_DISCO_ITEMS,
|
||||
?MODULE, process_local_iq_items, IQDisc),
|
||||
@@ -41,19 +48,27 @@ start(Opts) ->
|
||||
?MODULE, process_sm_iq_items, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_DISCO_INFO,
|
||||
?MODULE, process_sm_iq_info, IQDisc),
|
||||
|
||||
catch ets:new(disco_features, [named_table, ordered_set, public]),
|
||||
register_feature("iq"),
|
||||
register_feature("presence"),
|
||||
register_feature("presence-invisible"),
|
||||
|
||||
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() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_DISCO_ITEMS),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_DISCO_INFO),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_DISCO_ITEMS),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_DISCO_INFO).
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_DISCO_INFO),
|
||||
catch ets:delete(disco_features),
|
||||
catch ets:delete(disco_extra_domains),
|
||||
ok.
|
||||
|
||||
|
||||
register_feature(Feature) ->
|
||||
@@ -72,44 +87,44 @@ unregister_extra_domain(Domain) ->
|
||||
catch ets:new(disco_extra_domains, [named_table, ordered_set, public]),
|
||||
ets:delete(disco_extra_domains, Domain).
|
||||
|
||||
process_local_iq_items(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
Lang = xml:get_tag_attr_s("xml:lang", SubEl),
|
||||
process_local_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
SNode = xml:get_tag_attr_s("node", SubEl),
|
||||
Node = string:tokens(SNode, "/"),
|
||||
|
||||
case acl:match_rule(configure, From) of
|
||||
deny when Node /= [] ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
deny ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
||||
get_services_only()
|
||||
}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
||||
get_services_only()
|
||||
}]};
|
||||
_ ->
|
||||
case get_local_items(Node, jlib:jid_to_string(To), Lang) of
|
||||
{result, Res} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS},
|
||||
{"node", SNode}],
|
||||
Res
|
||||
}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS},
|
||||
{"node", SNode}],
|
||||
Res
|
||||
}]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS, [SubEl, Error]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
process_local_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_local_iq_info(From, _To, #iq{type = Type, xmlns = XMLNS,
|
||||
sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
SNode = xml:get_tag_attr_s("node", SubEl),
|
||||
Node = string:tokens(SNode, "/"),
|
||||
@@ -117,17 +132,18 @@ process_local_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
{_, []} ->
|
||||
Features = lists:map(fun feature_to_xml/1,
|
||||
ets:tab2list(disco_features)),
|
||||
{iq, ID, result, XMLNS, [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", ?NS_DISCO_INFO}],
|
||||
[{xmlelement, "identity",
|
||||
[{"category", "service"},
|
||||
{"type", "im"},
|
||||
{"name", "ejabberd"}], []}] ++
|
||||
Features
|
||||
}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", ?NS_DISCO_INFO}],
|
||||
[{xmlelement, "identity",
|
||||
[{"category", "server"},
|
||||
{"type", "im"},
|
||||
{"name", "ejabberd"}], []}] ++
|
||||
Features
|
||||
}]};
|
||||
{deny, _} ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
{allow, ["config"]} -> ?EMPTY_INFO_RESULT;
|
||||
{allow, ["online users"]} -> ?EMPTY_INFO_RESULT;
|
||||
{allow, ["all users"]} -> ?EMPTY_INFO_RESULT;
|
||||
@@ -136,56 +152,57 @@ process_local_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
{allow, ["running nodes"]} -> ?EMPTY_INFO_RESULT;
|
||||
{allow, ["stopped nodes"]} -> ?EMPTY_INFO_RESULT;
|
||||
{allow, ["running nodes", ENode]} ->
|
||||
{iq, ID, result, XMLNS, [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[{xmlelement, "identity",
|
||||
[{"category", "ejabberd"},
|
||||
{"type", "node"},
|
||||
{"name", ENode}], []},
|
||||
feature_to_xml({?NS_STATS})
|
||||
]
|
||||
}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[{xmlelement, "identity",
|
||||
[{"category", "ejabberd"},
|
||||
{"type", "node"},
|
||||
{"name", ENode}], []},
|
||||
feature_to_xml({?NS_STATS})
|
||||
]
|
||||
}]};
|
||||
{allow, ["running nodes", ENode, "DB"]} ->
|
||||
{iq, ID, result, XMLNS, [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[feature_to_xml({?NS_IQDATA})]}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
|
||||
{allow, ["running nodes", ENode, "modules"]} ->
|
||||
?EMPTY_INFO_RESULT;
|
||||
{allow, ["running nodes", ENode, "modules", _]} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[feature_to_xml({?NS_IQDATA})]}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
|
||||
{allow, ["running nodes", ENode, "backup"]} ->
|
||||
?EMPTY_INFO_RESULT;
|
||||
{allow, ["running nodes", ENode, "backup", _]} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[feature_to_xml({?NS_IQDATA})]}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
|
||||
{allow, ["running nodes", ENode, "import"]} ->
|
||||
?EMPTY_INFO_RESULT;
|
||||
{allow, ["running nodes", ENode, "import", _]} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[feature_to_xml({?NS_IQDATA})]}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
|
||||
{allow, ["config", _]} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[feature_to_xml({?NS_IQDATA})]}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", SNode}],
|
||||
[feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -227,10 +244,10 @@ get_local_items([], Server, Lang) ->
|
||||
|
||||
get_local_items(["config"], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("Host Name", "config/hostname"),
|
||||
?NODE("ACLs", "config/acls"),
|
||||
?NODE("Access Rules", "config/access"),
|
||||
?NODE("Remove Users", "config/remusers")
|
||||
[?NODE("Host Name", "config/hostname"),
|
||||
?NODE("Access Control Lists", "config/acls"),
|
||||
?NODE("Access Rules", "config/access"),
|
||||
?NODE("Remove Users", "config/remusers")
|
||||
]};
|
||||
|
||||
get_local_items(["config", _], Server, Lang) ->
|
||||
@@ -452,43 +469,108 @@ get_stopped_nodes(Lang) ->
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
process_sm_iq_items(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
#jid{user = User} = To,
|
||||
case {acl:match_rule(configure, From), Type} of
|
||||
{deny, _} ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
{allow, set} ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
{allow, get} ->
|
||||
case xml:get_tag_attr_s("node", SubEl) of
|
||||
"" ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}],
|
||||
get_user_resources(User)
|
||||
}]};
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
end
|
||||
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, _, _} ->
|
||||
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]}
|
||||
end.
|
||||
|
||||
|
||||
process_sm_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
case {acl:match_rule(configure, From), Type} of
|
||||
{deny, _} ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
{allow, set} ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
{allow, get} ->
|
||||
case xml:get_tag_attr_s("node", SubEl) of
|
||||
"" ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query", [{"xmlns", XMLNS}],
|
||||
[feature_to_xml({?NS_IQDATA})]}]};
|
||||
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, _, _} ->
|
||||
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]};
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]}
|
||||
end
|
||||
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]}
|
||||
end.
|
||||
|
||||
|
||||
@@ -501,3 +583,8 @@ get_user_resources(User) ->
|
||||
{"name", User}], []}
|
||||
end, lists:sort(Rs)).
|
||||
|
||||
node_to_xml(User, Node, Name) ->
|
||||
{xmlelement, "item", [{"jid", User ++ "@" ++ ?MYNAME},
|
||||
{"node", Node},
|
||||
{"name", Name}], []}.
|
||||
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ start(Opts) ->
|
||||
register(ejabberd_mod_echo, spawn(?MODULE, init, [Host])).
|
||||
|
||||
init(Host) ->
|
||||
ejabberd_router:register_local_route(Host),
|
||||
ejabberd_router:register_route(Host),
|
||||
loop(Host).
|
||||
|
||||
loop(Host) ->
|
||||
@@ -34,7 +34,7 @@ loop(Host) ->
|
||||
ejabberd_router:route(To, From, Packet),
|
||||
loop(Host);
|
||||
stop ->
|
||||
ejabberd_router:unregister_local_route(Host),
|
||||
ejabberd_router:unregister_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host)
|
||||
|
||||
+8
-15
@@ -1,21 +1,15 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@
|
||||
LIBS = @LIBS@ @ERLANG_LIBS@ @LIBICONV@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
ERLSHLIBS = ../iconv_erl.so
|
||||
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
@@ -28,21 +22,20 @@ all: $(OBJS) $(ERLSHLIBS)
|
||||
$(OUTDIR)/%.beam: %.erl
|
||||
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) $(LIBDIRS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) \
|
||||
-lerl_interface @LIBICONV@ \
|
||||
-lei \
|
||||
-o $@ -fpic -shared \
|
||||
$(CC) -Wall $(INCLUDES) $(CFLAGS) $(LDFLAGS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) $(LIBS) \
|
||||
-o $@ -fpic -shared
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(ERLSHLIBS)
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
|
||||
|
||||
+16
-23
@@ -1,8 +1,6 @@
|
||||
|
||||
include ..\Makefile.inc
|
||||
|
||||
GNUWIN_DIR="c:\progra~1\gnuwin32"
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
|
||||
@@ -11,16 +9,18 @@ OBJS = \
|
||||
$(OUTDIR)\mod_irc.beam \
|
||||
$(OUTDIR)\mod_irc_connection.beam
|
||||
|
||||
ALL : $(OUTDIR)\iconv_erl.dll $(OBJS)
|
||||
SOURCE = iconv_erl.c
|
||||
OBJECT = iconv_erl.o
|
||||
DLL = $(OUTDIR)\iconv_erl.dll
|
||||
|
||||
ALL : $(DLL) $(OBJS)
|
||||
|
||||
CLEAN :
|
||||
-@erase $(OUTDIR)\iconv_erl.dll
|
||||
-@erase $(DLL)
|
||||
-@erase $(OUTDIR)\iconv_erl.exp
|
||||
-@erase $(OUTDIR)\iconv_erl.lib
|
||||
-@erase iconv_erl.obj
|
||||
-@erase iconv_erl.pch
|
||||
-@erase vc60.idb
|
||||
-@erase *.beam
|
||||
-@erase $(OBJECT)
|
||||
-@erase $(OBJS)
|
||||
|
||||
$(OUTDIR)\iconv.beam : iconv.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) iconv.erl
|
||||
@@ -31,22 +31,15 @@ $(OUTDIR)\mod_irc.beam : mod_irc.erl
|
||||
$(OUTDIR)\mod_irc_connection.beam : mod_irc_connection.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc_connection.erl
|
||||
|
||||
CPP=cl.exe
|
||||
CPP_PROJ=/nologo /ML /W3 /GX /O2 /I "$(ERLANG_DIR)\usr\include" /I "$(EI_DIR)\include" /I "$(GNUWIN_DIR)\include" /D "WIN32" /D "NDEBUG" /D "_USRDLL" /D "_MBCS" /Fpiconv_erl.pch /YX /FD /c
|
||||
CC=cl.exe
|
||||
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include" -I"$(ICONV_DIR)\include"
|
||||
|
||||
.c.obj::
|
||||
$(CPP) @<<
|
||||
$(CPP_PROJ) $<
|
||||
<<
|
||||
LD=link.exe
|
||||
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" "$(ICONV_LIB)" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib
|
||||
|
||||
LINK32=link.exe
|
||||
LINK32_FLAGS=kernel32.lib "$(ERLANG_DIR)\usr\lib\erl_dll.lib" "$(EI_DIR)\lib\ei.lib" "$(EI_DIR)\lib\erl_interface.lib" "$(GNUWIN_DIR)\lib\libiconv.lib" /nologo /subsystem:console /dll /pdb:none /machine:I386 /out:$(OUTDIR)\iconv_erl.dll
|
||||
LINK32_OBJS=iconv_erl.obj
|
||||
$(DLL) : $(OBJECT)
|
||||
$(LD) $(LD_FLAGS) -out:$(DLL) $(OBJECT)
|
||||
|
||||
$(OUTDIR)\iconv_erl.dll : $(LINK32_OBJS)
|
||||
$(LINK32) @<<
|
||||
$(LINK32_FLAGS) $(LINK32_OBJS)
|
||||
<<
|
||||
|
||||
iconv_erl.obj : iconv_erl.c
|
||||
$(OBJECT) : $(SOURCE)
|
||||
$(CC) $(CC_FLAGS) -c -Fo$(OBJECT) $(SOURCE)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
ok = erl_ddll:load_driver(".", iconv_erl),
|
||||
ok = erl_ddll:load_driver(ejabberd:get_so_path(), iconv_erl),
|
||||
Port = open_port({spawn, iconv_erl}, []),
|
||||
ets:new(iconv_table, [set, public, named_table]),
|
||||
ets:insert(iconv_table, {port, Port}),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <erl_driver.h>
|
||||
#include <ei.h>
|
||||
#include <iconv.h>
|
||||
@@ -104,12 +105,9 @@ ErlDrvEntry iconv_driver_entry = {
|
||||
NULL /* F_PTR outputv, reserved */
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
DRIVER_INIT(iconv_erl) /* must match name in driver_entry */
|
||||
{
|
||||
return &iconv_driver_entry;
|
||||
return &iconv_driver_entry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+189
-109
@@ -12,7 +12,7 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1, init/1, stop/0, closed_conection/2,
|
||||
-export([start/1, init/2, stop/0, closed_conection/2,
|
||||
get_user_and_encoding/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -29,54 +29,90 @@ start(Opts) ->
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, irc_custom)}]),
|
||||
Host = gen_mod:get_opt(host, Opts, "irc." ++ ?MYNAME),
|
||||
register(ejabberd_mod_irc, spawn(?MODULE, init, [Host])).
|
||||
Access = gen_mod:get_opt(access, Opts, all),
|
||||
register(ejabberd_mod_irc, spawn(?MODULE, init, [Host, Access])).
|
||||
|
||||
init(Host) ->
|
||||
init(Host, Access) ->
|
||||
catch ets:new(irc_connection, [named_table,
|
||||
public,
|
||||
{keypos, #irc_connection.userserver}]),
|
||||
ejabberd_router:register_route(Host),
|
||||
loop(Host).
|
||||
loop(Host, Access).
|
||||
|
||||
loop(Host) ->
|
||||
loop(Host, Access) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(Host, From, To, Packet) of
|
||||
case catch do_route(Host, Access, From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Host);
|
||||
loop(Host, Access);
|
||||
stop ->
|
||||
ejabberd_router:unregister_global_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host)
|
||||
loop(Host, Access)
|
||||
end.
|
||||
|
||||
|
||||
do_route(Host, From, To, Packet) ->
|
||||
do_route(Host, Access, From, To, Packet) ->
|
||||
case acl:match_rule(Access, From) of
|
||||
allow ->
|
||||
do_route1(Host, From, To, Packet);
|
||||
_ ->
|
||||
{xmlelement, _Name, Attrs, _Els} = Packet,
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
ErrText = "Access denied by service policy",
|
||||
Err = jlib:make_error_reply(Packet,
|
||||
?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end.
|
||||
|
||||
do_route1(Host, From, To, Packet) ->
|
||||
#jid{user = ChanServ, resource = Resource} = To,
|
||||
{xmlelement, _Name, Attrs, _Els} = Packet,
|
||||
case ChanServ of
|
||||
"" ->
|
||||
case Resource of
|
||||
"" ->
|
||||
case jlib:iq_query_info(Packet) of
|
||||
{iq, ID, get, ?NS_DISCO_INFO = XMLNS, SubEl} ->
|
||||
Res = {iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_disco()}]},
|
||||
#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
|
||||
sub_el = SubEl} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_disco()}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
{iq, ID, Type, ?NS_IQDATA = XMLNS, SubEl} ->
|
||||
iq_data(From, To, ID, XMLNS, Type, SubEl);
|
||||
_ ->
|
||||
#iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
[]}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
process_register(From, To, IQ);
|
||||
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
||||
lang = Lang} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el =
|
||||
[{xmlelement, "vCard",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_vcard(Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
#iq{} = IQ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST),
|
||||
@@ -151,12 +187,22 @@ iq_disco() ->
|
||||
{xmlelement, "feature",
|
||||
[{"var", ?NS_MUC}], []},
|
||||
{xmlelement, "feature",
|
||||
[{"var", ?NS_IQDATA}], []}].
|
||||
[{"var", ?NS_REGISTER}], []},
|
||||
{xmlelement, "feature",
|
||||
[{"var", ?NS_VCARD}], []}].
|
||||
|
||||
iq_get_vcard(Lang) ->
|
||||
[{xmlelement, "FN", [],
|
||||
[{xmlcdata, "ejabberd/mod_irc"}]},
|
||||
{xmlelement, "URL", [],
|
||||
[{xmlcdata,
|
||||
"http://ejabberd.jabberstudio.org/"}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module\n"
|
||||
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||
|
||||
|
||||
iq_data(From, To, ID, XMLNS, Type, SubEl) ->
|
||||
case catch process_iq_data(From, To, ID, XMLNS, Type, SubEl) of
|
||||
process_register(From, To, #iq{} = IQ) ->
|
||||
case catch process_irc_register(From, To, IQ) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
ResIQ ->
|
||||
@@ -169,54 +215,78 @@ iq_data(From, To, ID, XMLNS, Type, SubEl) ->
|
||||
end
|
||||
end.
|
||||
|
||||
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||
find_xdata_el1(SubEls).
|
||||
|
||||
process_iq_data(From, To, ID, XMLNS, Type, SubEl) ->
|
||||
Lang = xml:get_tag_attr_s("xml:lang", SubEl),
|
||||
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).
|
||||
|
||||
process_irc_register(From, To,
|
||||
#iq{type = Type, xmlns = XMLNS,
|
||||
lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
case xml:get_tag_attr_s("type", SubEl) of
|
||||
"cancel" ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query", [{"xmlns", XMLNS}], []}]};
|
||||
"submit" ->
|
||||
XData = jlib:parse_xdata_submit(SubEl),
|
||||
case XData of
|
||||
invalid ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_BAD_REQUEST]};
|
||||
XDataEl = find_xdata_el(SubEl),
|
||||
case XDataEl of
|
||||
false ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
|
||||
{xmlelement, _Name, Attrs, SubEls} ->
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"cancel" ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}], []}]};
|
||||
"submit" ->
|
||||
XData = jlib:parse_xdata_submit(XDataEl),
|
||||
case XData of
|
||||
invalid ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_BAD_REQUEST]};
|
||||
_ ->
|
||||
Node = string:tokens(
|
||||
xml:get_tag_attr_s("node", SubEl),
|
||||
"/"),
|
||||
case set_form(From, Node, Lang, XData) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
{error, Error} ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, Error]}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
Node =
|
||||
string:tokens(
|
||||
xml:get_tag_attr_s("node", SubEl),
|
||||
"/"),
|
||||
case set_form(From, Node, Lang, XData) of
|
||||
{result, Res} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, Error]}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_BAD_REQUEST]}
|
||||
end
|
||||
end;
|
||||
get ->
|
||||
Node =
|
||||
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||
case get_form(From, Node, Lang) of
|
||||
{result, Res} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query", [{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
Res
|
||||
}]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, Error]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, Error]}
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -240,54 +310,64 @@ get_form(From, [], Lang) ->
|
||||
Customs;
|
||||
{Username, Encodings} ->
|
||||
{result,
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
User ++ "@" ++ Server ++ " " ++
|
||||
translate:translate(Lang, "Configuration")}]},
|
||||
%{xmlelement, "instructions", [],
|
||||
% [{xmlcdata,
|
||||
% translate:translate(
|
||||
% Lang, "")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "IRC Username")},
|
||||
{"var", "username"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, Username}]}]},
|
||||
{xmlelement, "field", [{"type", "fixed"}],
|
||||
[{xmlelement, "value", [],
|
||||
[{xmlcdata,
|
||||
lists:flatten(
|
||||
io_lib:format(
|
||||
translate:translate(
|
||||
Lang,
|
||||
"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."),
|
||||
[?DEFAULT_IRC_ENCODING]))}]}]},
|
||||
{xmlelement, "field", [{"type", "fixed"}],
|
||||
[{xmlelement, "value", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang,
|
||||
"You need an x:data capable client "
|
||||
"to configure mod_irc settings")}]},
|
||||
{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang,
|
||||
"Example: [{\"irc.lucky.net\", \"koi8-r\"}, "
|
||||
"{\"vendetta.fef.net\", \"iso8859-1\"}]."
|
||||
)}]}]},
|
||||
{xmlelement, "field", [{"type", "text-multi"},
|
||||
{"label",
|
||||
translate:translate(Lang, "Encodings")},
|
||||
{"var", "encodings"}],
|
||||
lists:map(
|
||||
fun(S) ->
|
||||
{xmlelement, "value", [], [{xmlcdata, S}]}
|
||||
end,
|
||||
string:tokens(
|
||||
lists:flatten(
|
||||
io_lib:format("~p.", [Encodings])),
|
||||
"\n"))
|
||||
}
|
||||
]}
|
||||
"Registration in mod_irc for ") ++ User ++ "@" ++ Server}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang,
|
||||
"Enter username and encodings you wish to use for "
|
||||
"connecting to IRC servers")}]},
|
||||
{xmlelement, "field", [{"type", "text-single"},
|
||||
{"label",
|
||||
translate:translate(
|
||||
Lang, "IRC Username")},
|
||||
{"var", "username"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, Username}]}]},
|
||||
{xmlelement, "field", [{"type", "fixed"}],
|
||||
[{xmlelement, "value", [],
|
||||
[{xmlcdata,
|
||||
lists:flatten(
|
||||
io_lib:format(
|
||||
translate:translate(
|
||||
Lang,
|
||||
"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."),
|
||||
[?DEFAULT_IRC_ENCODING]))}]}]},
|
||||
{xmlelement, "field", [{"type", "fixed"}],
|
||||
[{xmlelement, "value", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang,
|
||||
"Example: [{\"irc.lucky.net\", \"koi8-r\"}, "
|
||||
"{\"vendetta.fef.net\", \"iso8859-1\"}]."
|
||||
)}]}]},
|
||||
{xmlelement, "field", [{"type", "text-multi"},
|
||||
{"label",
|
||||
translate:translate(Lang, "Encodings")},
|
||||
{"var", "encodings"}],
|
||||
lists:map(
|
||||
fun(S) ->
|
||||
{xmlelement, "value", [], [{xmlcdata, S}]}
|
||||
end,
|
||||
string:tokens(
|
||||
lists:flatten(
|
||||
io_lib:format("~p.", [Encodings])),
|
||||
"\n"))
|
||||
}
|
||||
]}]}
|
||||
end;
|
||||
|
||||
|
||||
@@ -323,16 +403,16 @@ set_form(From, [], Lang, XData) ->
|
||||
{atomic, _} ->
|
||||
{result, []};
|
||||
_ ->
|
||||
{error, "406", "Not Acceptable"}
|
||||
{error, ?ERR_NOT_ACCEPTABLE}
|
||||
end;
|
||||
_ ->
|
||||
{error, "406", "Not Acceptable"}
|
||||
{error, ?ERR_NOT_ACCEPTABLE}
|
||||
end;
|
||||
_ ->
|
||||
{error, "406", "Not Acceptable"}
|
||||
{error, ?ERR_NOT_ACCEPTABLE}
|
||||
end;
|
||||
_ ->
|
||||
{error, "406", "Not Acceptable"}
|
||||
{error, ?ERR_NOT_ACCEPTABLE}
|
||||
end;
|
||||
|
||||
|
||||
|
||||
@@ -234,20 +234,27 @@ handle_info({route_chan, Channel, Resource,
|
||||
StateData#state.myname, StateData#state.nick),
|
||||
StateData#state.user, El),
|
||||
Body = xml:get_path_s(El, [{elem, "body"}, cdata]),
|
||||
Body1 = case Body of
|
||||
[$/, $m, $e, $ | Rest] ->
|
||||
"\001ACTION " ++ Rest ++ "\001";
|
||||
_ ->
|
||||
Body
|
||||
end,
|
||||
Strings = string:tokens(Body1, "\n"),
|
||||
Res = lists:concat(
|
||||
lists:map(
|
||||
fun(S) ->
|
||||
io_lib:format("PRIVMSG #~s :~s\r\n",
|
||||
[Channel, S])
|
||||
end, Strings)),
|
||||
?SEND(Res);
|
||||
case Body of
|
||||
"/quote " ++ Rest ->
|
||||
?SEND(Rest ++ "\r\n");
|
||||
_ ->
|
||||
Body1 =
|
||||
case Body of
|
||||
[$/, $m, $e, $ | Rest] ->
|
||||
"\001ACTION " ++ Rest ++ "\001";
|
||||
_ ->
|
||||
Body
|
||||
end,
|
||||
Strings = string:tokens(Body1, "\n"),
|
||||
Res = lists:concat(
|
||||
lists:map(
|
||||
fun(S) ->
|
||||
io_lib:format(
|
||||
"PRIVMSG #~s :~s\r\n",
|
||||
[Channel, S])
|
||||
end, Strings)),
|
||||
?SEND(Res)
|
||||
end;
|
||||
Subject ->
|
||||
Strings = string:tokens(Subject, "\n"),
|
||||
Res = lists:concat(
|
||||
@@ -260,20 +267,25 @@ handle_info({route_chan, Channel, Resource,
|
||||
end;
|
||||
"chat" ->
|
||||
Body = xml:get_path_s(El, [{elem, "body"}, cdata]),
|
||||
Body1 = case Body of
|
||||
[$/, $m, $e, $ | Rest] ->
|
||||
"\001ACTION " ++ Rest ++ "\001";
|
||||
_ ->
|
||||
Body
|
||||
end,
|
||||
Strings = string:tokens(Body1, "\n"),
|
||||
Res = lists:concat(
|
||||
lists:map(
|
||||
fun(S) ->
|
||||
io_lib:format("PRIVMSG ~s :~s\r\n",
|
||||
[Resource, S])
|
||||
end, Strings)),
|
||||
?SEND(Res);
|
||||
case Body of
|
||||
"/quote " ++ Rest ->
|
||||
?SEND(Rest ++ "\r\n");
|
||||
_ ->
|
||||
Body1 = case Body of
|
||||
[$/, $m, $e, $ | Rest] ->
|
||||
"\001ACTION " ++ Rest ++ "\001";
|
||||
_ ->
|
||||
Body
|
||||
end,
|
||||
Strings = string:tokens(Body1, "\n"),
|
||||
Res = lists:concat(
|
||||
lists:map(
|
||||
fun(S) ->
|
||||
io_lib:format("PRIVMSG ~s :~s\r\n",
|
||||
[Resource, S])
|
||||
end, Strings)),
|
||||
?SEND(Res)
|
||||
end;
|
||||
"error" ->
|
||||
stop;
|
||||
_ ->
|
||||
@@ -291,32 +303,31 @@ handle_info({route_chan, Channel, Resource,
|
||||
{xmlelement, "iq", Attrs, Els} = El},
|
||||
StateName, StateData) ->
|
||||
From = StateData#state.user,
|
||||
To = {lists:concat([Channel, "%", StateData#state.server]),
|
||||
StateData#state.myname, StateData#state.nick},
|
||||
To = jlib:make_jid(lists:concat([Channel, "%", StateData#state.server]),
|
||||
StateData#state.myname, StateData#state.nick),
|
||||
case jlib:iq_query_info(El) of
|
||||
{iq, ID, Type, ?NS_MUC_ADMIN = XMLNS, SubEl} ->
|
||||
iq_admin(StateData, Channel,
|
||||
From,
|
||||
To,
|
||||
ID, XMLNS, Type, SubEl);
|
||||
{iq, ID, get, ?NS_VERSION = XMLNS, SubEl} ->
|
||||
#iq{xmlns = ?NS_MUC_ADMIN} = IQ ->
|
||||
iq_admin(StateData, Channel, From, To, IQ);
|
||||
#iq{xmlns = ?NS_VERSION} ->
|
||||
Res = io_lib:format("PRIVMSG ~s :\001VERSION\001\r\n",
|
||||
[Resource]),
|
||||
?SEND(Res),
|
||||
Err = jlib:make_error_reply(
|
||||
El, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
{iq, ID, get, ?NS_TIME = XMLNS, SubEl} ->
|
||||
#iq{xmlns = ?NS_TIME} ->
|
||||
Res = io_lib:format("PRIVMSG ~s :\001TIME\001\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),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
ejabberd_router:route(To, From, Err);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{next_state, StateName, StateData};
|
||||
|
||||
@@ -331,20 +342,25 @@ handle_info({route_nick, Nick,
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"chat" ->
|
||||
Body = xml:get_path_s(El, [{elem, "body"}, cdata]),
|
||||
Body1 = case Body of
|
||||
[$/, $m, $e, $ | Rest] ->
|
||||
"\001ACTION " ++ Rest ++ "\001";
|
||||
_ ->
|
||||
Body
|
||||
end,
|
||||
Strings = string:tokens(Body1, "\n"),
|
||||
Res = lists:concat(
|
||||
lists:map(
|
||||
fun(S) ->
|
||||
io_lib:format("PRIVMSG ~s :~s\r\n",
|
||||
[Nick, S])
|
||||
end, Strings)),
|
||||
?SEND(Res);
|
||||
case Body of
|
||||
"/quote " ++ Rest ->
|
||||
?SEND(Rest ++ "\r\n");
|
||||
_ ->
|
||||
Body1 = case Body of
|
||||
[$/, $m, $e, $ | Rest] ->
|
||||
"\001ACTION " ++ Rest ++ "\001";
|
||||
_ ->
|
||||
Body
|
||||
end,
|
||||
Strings = string:tokens(Body1, "\n"),
|
||||
Res = lists:concat(
|
||||
lists:map(
|
||||
fun(S) ->
|
||||
io_lib:format("PRIVMSG ~s :~s\r\n",
|
||||
[Nick, S])
|
||||
end, Strings)),
|
||||
?SEND(Res)
|
||||
end;
|
||||
"error" ->
|
||||
stop;
|
||||
_ ->
|
||||
@@ -976,7 +992,8 @@ remove_element(E, Set) ->
|
||||
|
||||
|
||||
|
||||
iq_admin(StateData, Channel, From, To, ID, XMLNS, Type, SubEl) ->
|
||||
iq_admin(StateData, Channel, From, To,
|
||||
#iq{type = Type, xmlns = XMLNS, sub_el = SubEl} = IQ) ->
|
||||
case catch process_iq_admin(StateData, Channel, Type, SubEl) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
@@ -985,14 +1002,14 @@ iq_admin(StateData, Channel, From, To, ID, XMLNS, Type, SubEl) ->
|
||||
Res /= ignore ->
|
||||
ResIQ = case Res of
|
||||
{result, ResEls} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
ResEls
|
||||
}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
ResEls
|
||||
}]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, Error]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, Error]}
|
||||
end,
|
||||
ejabberd_router:route(To, From,
|
||||
jlib:iq_to_xml(ResIQ));
|
||||
|
||||
+58
-34
@@ -16,13 +16,13 @@
|
||||
stop/0,
|
||||
process_local_iq/3,
|
||||
process_sm_iq/3,
|
||||
on_presence_update/1,
|
||||
on_presence_update/2,
|
||||
remove_user/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(last_activity, {user, timestamp}).
|
||||
-record(last_activity, {user, timestamp, status}).
|
||||
|
||||
|
||||
start(Opts) ->
|
||||
@@ -30,33 +30,34 @@ start(Opts) ->
|
||||
mnesia:create_table(last_activity,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, last_activity)}]),
|
||||
update_table(),
|
||||
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).
|
||||
|
||||
stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_LAST).
|
||||
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, ID, Type, XMLNS, SubEl}) ->
|
||||
process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_LAST},
|
||||
{"seconds", integer_to_list(Sec)}],
|
||||
[]}]}
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_LAST},
|
||||
{"seconds", integer_to_list(Sec)}],
|
||||
[]}]}
|
||||
end.
|
||||
|
||||
|
||||
process_sm_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
User = To#jid.luser,
|
||||
{Subscription, _Groups} =
|
||||
@@ -65,50 +66,54 @@ process_sm_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
(Subscription == both) or (Subscription == from) ->
|
||||
case catch mod_privacy:get_user_list(User) of
|
||||
{'EXIT', _Reason} ->
|
||||
get_last(ID, XMLNS, SubEl, User);
|
||||
get_last(IQ, SubEl, User);
|
||||
List ->
|
||||
case mod_privacy:check_packet(
|
||||
User, List,
|
||||
{From, To,
|
||||
{xmlelement, "presence", [], []}},
|
||||
out) of
|
||||
case catch mod_privacy:check_packet(
|
||||
User, List,
|
||||
{From, To,
|
||||
{xmlelement, "presence", [], []}},
|
||||
out) of
|
||||
{'EXIT', _Reason} ->
|
||||
get_last(IQ, SubEl, User);
|
||||
allow ->
|
||||
get_last(ID, XMLNS, SubEl, User);
|
||||
get_last(IQ, SubEl, User);
|
||||
deny ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end
|
||||
end;
|
||||
true ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end
|
||||
end.
|
||||
|
||||
get_last(ID, XMLNS, SubEl, LUser) ->
|
||||
get_last(IQ, SubEl, LUser) ->
|
||||
case catch mnesia:dirty_read(last_activity, LUser) of
|
||||
{'EXIT', _Reason} ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
|
||||
[] ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
|
||||
[#last_activity{timestamp = TimeStamp}] ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
|
||||
[#last_activity{timestamp = TimeStamp, status = Status}] ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
TimeStamp2 = MegaSecs * 1000000 + Secs,
|
||||
Sec = TimeStamp2 - TimeStamp,
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_LAST},
|
||||
{"seconds", integer_to_list(Sec)}],
|
||||
[]}]}
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_LAST},
|
||||
{"seconds", integer_to_list(Sec)}],
|
||||
[{xmlcdata, Status}]}]}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
on_presence_update(LUser) ->
|
||||
on_presence_update(LUser, Status) ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
TimeStamp = MegaSecs * 1000000 + Secs,
|
||||
F = fun() ->
|
||||
mnesia:write(#last_activity{user = LUser,
|
||||
timestamp = TimeStamp})
|
||||
timestamp = TimeStamp,
|
||||
status = Status})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
@@ -119,3 +124,22 @@ remove_user(User) ->
|
||||
mnesia:delete({last_activity, LUser})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, last_activity),
|
||||
case mnesia:table_info(last_activity, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, timestamp] ->
|
||||
?INFO_MSG("Converting last_activity table from {user, timestamp} format", []),
|
||||
mnesia:transform_table(
|
||||
last_activity,
|
||||
fun({_, U, T}) ->
|
||||
#last_activity{user = U, timestamp = T, status = ""}
|
||||
end, Fields);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating last_activity table", []),
|
||||
mnesia:transform_table(last_activity, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@
|
||||
LIBS = @LIBS@ @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
@@ -24,10 +19,12 @@ all: $(OBJS)
|
||||
$(OUTDIR)/%.beam: %.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ OBJS = \
|
||||
ALL : $(OBJS)
|
||||
|
||||
CLEAN :
|
||||
-@erase *.beam
|
||||
-@erase $(OBJS)
|
||||
|
||||
$(OUTDIR)\mod_muc.beam : mod_muc.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc.erl
|
||||
|
||||
+145
-96
@@ -13,13 +13,13 @@
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1,
|
||||
init/1,
|
||||
init/2,
|
||||
stop/0,
|
||||
room_destroyed/1,
|
||||
store_room/2,
|
||||
restore_room/1,
|
||||
forget_room/1,
|
||||
process_iq_disco_items/5,
|
||||
process_iq_disco_items/4,
|
||||
can_use_nick/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -40,43 +40,63 @@ start(Opts) ->
|
||||
{attributes, record_info(fields, muc_registered)}]),
|
||||
mnesia:add_table_index(muc_registered, nick),
|
||||
Host = gen_mod:get_opt(host, Opts, "conference." ++ ?MYNAME),
|
||||
register(ejabberd_mod_muc, spawn(?MODULE, init, [Host])).
|
||||
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(ejabberd_mod_muc,
|
||||
spawn(?MODULE, init, [Host, {Access, AccessCreate, AccessAdmin}])).
|
||||
|
||||
|
||||
|
||||
init(Host) ->
|
||||
init(Host, Access) ->
|
||||
catch ets:new(muc_online_room, [named_table,
|
||||
public,
|
||||
{keypos, #muc_online_room.name}]),
|
||||
ejabberd_router:register_route(Host),
|
||||
load_permanent_rooms(Host),
|
||||
loop(Host).
|
||||
load_permanent_rooms(Host, Access),
|
||||
loop(Host, Access).
|
||||
|
||||
loop(Host) ->
|
||||
loop(Host, Access) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(Host, From, To, Packet) of
|
||||
case catch do_route(Host, Access, From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Host);
|
||||
loop(Host, Access);
|
||||
{room_destroyed, Room} ->
|
||||
ets:delete(muc_online_room, Room),
|
||||
loop(Host);
|
||||
loop(Host, Access);
|
||||
stop ->
|
||||
% TODO
|
||||
ejabberd_router:unregister_global_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host)
|
||||
loop(Host, Access)
|
||||
end.
|
||||
|
||||
|
||||
do_route(Host, From, To, Packet) ->
|
||||
do_route(Host, Access, From, To, Packet) ->
|
||||
{AccessRoute, _AccessCreate, _AccessAdmin} = Access,
|
||||
case acl:match_rule(AccessRoute, From) of
|
||||
allow ->
|
||||
do_route1(Host, Access, From, To, Packet);
|
||||
_ ->
|
||||
{xmlelement, _Name, Attrs, _Els} = Packet,
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
ErrText = "Access denied by service policy",
|
||||
Err = jlib:make_error_reply(Packet,
|
||||
?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end.
|
||||
|
||||
|
||||
do_route1(Host, Access, From, To, Packet) ->
|
||||
{_AccessRoute, AccessCreate, AccessAdmin} = Access,
|
||||
{Room, _, Nick} = jlib:jid_tolower(To),
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
{xmlelement, Name, Attrs, _Els} = Packet,
|
||||
case Room of
|
||||
"" ->
|
||||
case Nick of
|
||||
@@ -84,36 +104,44 @@ do_route(Host, From, To, Packet) ->
|
||||
case Name of
|
||||
"iq" ->
|
||||
case jlib:iq_query_info(Packet) of
|
||||
{iq, ID, get, ?NS_DISCO_INFO = XMLNS, SubEl} ->
|
||||
Res = {iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_disco_info()}]},
|
||||
#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
|
||||
sub_el = SubEl} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_disco_info()}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
{iq, ID, get, ?NS_DISCO_ITEMS = XMLNS, SubEl} ->
|
||||
#iq{type = get,
|
||||
xmlns = ?NS_DISCO_ITEMS} = IQ ->
|
||||
spawn(?MODULE,
|
||||
process_iq_disco_items,
|
||||
[Host, From, To, ID, SubEl]);
|
||||
{iq, ID, get, ?NS_REGISTER = XMLNS, SubEl} ->
|
||||
Lang = xml:get_tag_attr_s(
|
||||
"xml:lang", SubEl),
|
||||
Res = {iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_register_info(
|
||||
From, Lang)}]},
|
||||
[Host, From, To, IQ]);
|
||||
#iq{type = get,
|
||||
xmlns = ?NS_REGISTER = XMLNS,
|
||||
lang = Lang,
|
||||
sub_el = SubEl} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el =
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_register_info(
|
||||
From, Host, Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
{iq, ID, set, ?NS_REGISTER = XMLNS, SubEl} ->
|
||||
case process_iq_register_set(From, SubEl) of
|
||||
#iq{type = set,
|
||||
xmlns = ?NS_REGISTER = XMLNS,
|
||||
lang = Lang,
|
||||
sub_el = SubEl} = IQ ->
|
||||
case process_iq_register_set(From, SubEl, Lang) of
|
||||
{result, IQRes} ->
|
||||
Res = {iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
IQRes}]},
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el =
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
IQRes}]},
|
||||
ejabberd_router:route(
|
||||
To, From, jlib:iq_to_xml(Res));
|
||||
{error, Error} ->
|
||||
@@ -122,39 +150,44 @@ do_route(Host, From, To, Packet) ->
|
||||
ejabberd_router:route(
|
||||
To, From, Err)
|
||||
end;
|
||||
{iq, ID, get, ?NS_VCARD = XMLNS, SubEl} ->
|
||||
Lang = xml:get_tag_attr_s(
|
||||
"xml:lang", SubEl),
|
||||
Res = {iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_vcard(Lang)}]},
|
||||
#iq{type = get,
|
||||
xmlns = ?NS_VCARD = XMLNS,
|
||||
lang = Lang,
|
||||
sub_el = SubEl} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el =
|
||||
[{xmlelement, "vCard",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_vcard(Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
reply ->
|
||||
ok;
|
||||
_ ->
|
||||
#iq{} ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet,
|
||||
?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
ejabberd_router:route(To, From, Err);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
"message" ->
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"error" ->
|
||||
ok;
|
||||
_ ->
|
||||
case acl:match_rule(muc_admin, From) of
|
||||
case acl:match_rule(AccessAdmin, From) of
|
||||
allow ->
|
||||
Msg = xml:get_path_s(
|
||||
Packet,
|
||||
[{elem, "body"}, cdata]),
|
||||
broadcast_service_message(Msg);
|
||||
_ ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
ErrText = "Only service administrators "
|
||||
"are allowed to send service messages",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet,
|
||||
?ERR_NOT_ALLOWED),
|
||||
?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
To, From, Err)
|
||||
end
|
||||
@@ -180,17 +213,28 @@ do_route(Host, From, To, Packet) ->
|
||||
Type = xml:get_attr_s("type", Attrs),
|
||||
case {Name, Type} of
|
||||
{"presence", ""} ->
|
||||
?DEBUG("MUC: open new room '~s'~n", [Room]),
|
||||
{ok, Pid} = mod_muc_room:start(
|
||||
Host, Room, From, Nick),
|
||||
ets:insert(
|
||||
muc_online_room,
|
||||
#muc_online_room{name = Room, pid = Pid}),
|
||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||
ok;
|
||||
case acl:match_rule(AccessCreate, From) of
|
||||
allow ->
|
||||
?DEBUG("MUC: open new room '~s'~n", [Room]),
|
||||
{ok, Pid} = mod_muc_room:start(
|
||||
Host, Access, Room, From, Nick),
|
||||
ets:insert(
|
||||
muc_online_room,
|
||||
#muc_online_room{name = Room, pid = Pid}),
|
||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||
ok;
|
||||
_ ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
ErrText = "Room creation is denied by service policy",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
_ ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
ErrText = "Conference room does not exist",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
[R] ->
|
||||
@@ -236,7 +280,7 @@ forget_room(Name) ->
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
load_permanent_rooms(Host) ->
|
||||
load_permanent_rooms(Host, Access) ->
|
||||
case catch mnesia:dirty_select(muc_room, [{'_', [], ['$_']}]) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]),
|
||||
@@ -246,6 +290,7 @@ load_permanent_rooms(Host) ->
|
||||
Room = R#muc_room.name,
|
||||
{ok, Pid} = mod_muc_room:start(
|
||||
Host,
|
||||
Access,
|
||||
Room,
|
||||
R#muc_room.opts),
|
||||
ets:insert(
|
||||
@@ -265,19 +310,19 @@ iq_disco_info() ->
|
||||
{xmlelement, "feature", [{"var", ?NS_VCARD}], []}].
|
||||
|
||||
|
||||
process_iq_disco_items(Host, From, To, ID, SubEl) ->
|
||||
Res = {iq, ID, result, ?NS_DISCO_ITEMS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
||||
iq_disco_items(Host, From)}]},
|
||||
process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
||||
iq_disco_items(Host, From, Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res)).
|
||||
|
||||
iq_disco_items(Host, From) ->
|
||||
iq_disco_items(Host, From, Lang) ->
|
||||
lists:zf(fun(#muc_online_room{name = Name, pid = Pid}) ->
|
||||
case catch gen_fsm:sync_send_all_state_event(
|
||||
Pid, {get_disco_item, From}, 100) of
|
||||
Pid, {get_disco_item, From, Lang}, 100) of
|
||||
{item, Desc} ->
|
||||
{true,
|
||||
{xmlelement, "item",
|
||||
@@ -295,38 +340,41 @@ iq_disco_items(Host, From) ->
|
||||
{"var", Var}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
|
||||
|
||||
iq_get_register_info(From, Lang) ->
|
||||
iq_get_register_info(From, Host, Lang) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||
LUS = {LUser, LServer},
|
||||
Nick = case catch mnesia:dirty_read(muc_registered, LUS) of
|
||||
{'EXIT', Reason} ->
|
||||
"";
|
||||
{Nick, Registered} = case catch mnesia:dirty_read(muc_registered, LUS) of
|
||||
{'EXIT', _Reason} ->
|
||||
{"", []};
|
||||
[] ->
|
||||
"";
|
||||
{"", []};
|
||||
[#muc_registered{nick = N}] ->
|
||||
N
|
||||
{N, [{xmlelement, "registered", [], []}]}
|
||||
end,
|
||||
[{xmlelement, "instructions", [],
|
||||
[{xmlcdata, translate:translate(
|
||||
Lang, "You need a x:data capable client to register.")}]},
|
||||
{xmlelement, "x",
|
||||
[{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Nick Registration")}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter nick you want to register.")}]},
|
||||
?XFIELD("text-single", "Nick", "nick", Nick)]}].
|
||||
Registered ++
|
||||
[{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "You need an x:data capable client to register nickname")}]},
|
||||
{xmlelement, "x",
|
||||
[{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Nickname Registration at ") ++ Host}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "Enter nickname you want to register")}]},
|
||||
?XFIELD("text-single", "Nickname", "nick", Nick)]}].
|
||||
|
||||
iq_set_register_info(From, XData) ->
|
||||
iq_set_register_info(From, XData, Lang) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||
LUS = {LUser, LServer},
|
||||
case lists:keysearch("nick", 1, XData) of
|
||||
false ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
ErrText = "You must fill in field \"nick\" in the form",
|
||||
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
|
||||
{value, {_, [Nick]}} ->
|
||||
F = fun() ->
|
||||
case Nick of
|
||||
@@ -358,16 +406,17 @@ iq_set_register_info(From, XData) ->
|
||||
{atomic, ok} ->
|
||||
{result, []};
|
||||
{atomic, false} ->
|
||||
{error, ?ERR_NOT_ALLOWED};
|
||||
ErrText = "Specified nickname is already registered",
|
||||
{error, ?ERRT_CONFLICT(Lang, ErrText)};
|
||||
_ ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
end
|
||||
end.
|
||||
|
||||
process_iq_register_set(From, SubEl) ->
|
||||
{xmlelement, Name, Attrs, Els} = SubEl,
|
||||
process_iq_register_set(From, SubEl, Lang) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
||||
case xml:remove_cdata(Els) of
|
||||
[{xmlelement, "x", Attrs1, Els1} = XEl] ->
|
||||
[{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
|
||||
case {xml:get_tag_attr_s("xmlns", XEl),
|
||||
xml:get_tag_attr_s("type", XEl)} of
|
||||
{?NS_XDATA, "cancel"} ->
|
||||
@@ -378,7 +427,7 @@ process_iq_register_set(From, SubEl) ->
|
||||
invalid ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
iq_set_register_info(From, XData)
|
||||
iq_set_register_info(From, XData, Lang)
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
@@ -394,20 +443,20 @@ iq_get_vcard(Lang) ->
|
||||
[{xmlcdata,
|
||||
"http://ejabberd.jabberstudio.org/"}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata, "ejabberd MUC module\n"
|
||||
"Copyright (c) 2003 Alexey Shchepin"}]}].
|
||||
[{xmlcdata, translate:translate(Lang, "ejabberd MUC module\n"
|
||||
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||
|
||||
|
||||
broadcast_service_message(Msg) ->
|
||||
lists:foreach(
|
||||
fun(#muc_online_room{name = Name, pid = Pid}) ->
|
||||
fun(#muc_online_room{pid = Pid}) ->
|
||||
gen_fsm:send_all_state_event(
|
||||
Pid, {service_message, Msg})
|
||||
end, ets:tab2list(muc_online_room)).
|
||||
|
||||
|
||||
|
||||
can_use_nick(JID, "") ->
|
||||
can_use_nick(_JID, "") ->
|
||||
false;
|
||||
can_use_nick(JID, Nick) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(JID),
|
||||
@@ -415,7 +464,7 @@ can_use_nick(JID, Nick) ->
|
||||
case catch mnesia:dirty_index_read(muc_registered,
|
||||
Nick,
|
||||
#muc_registered.nick) of
|
||||
{'EXIT', Reason} ->
|
||||
{'EXIT', _Reason} ->
|
||||
true;
|
||||
[] ->
|
||||
true;
|
||||
|
||||
+481
-183
File diff suppressed because it is too large
Load Diff
+156
-13
@@ -16,20 +16,31 @@
|
||||
stop/0,
|
||||
store_packet/3,
|
||||
resend_offline_messages/1,
|
||||
pop_offline_messages/2,
|
||||
remove_expired_messages/0,
|
||||
remove_old_messages/1,
|
||||
remove_user/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(offline_msg, {user, timestamp, from, to, packet}).
|
||||
-record(offline_msg, {user, timestamp, expire, 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),
|
||||
register(?PROCNAME, spawn(?MODULE, init, [])).
|
||||
|
||||
init() ->
|
||||
@@ -39,7 +50,14 @@ 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)
|
||||
@@ -60,23 +78,36 @@ 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),
|
||||
exit(whereis(?PROCNAME), stop),
|
||||
ok.
|
||||
|
||||
store_packet(From, To, Packet) ->
|
||||
true = is_process_alive(whereis(?PROCNAME)),
|
||||
Type = xml:get_tag_attr_s("type", Packet),
|
||||
true = Type /= "error" andalso Type /= "groupchat",
|
||||
case check_event(From, To, Packet) of
|
||||
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 ->
|
||||
#jid{luser = LUser} = To,
|
||||
TimeStamp = now(),
|
||||
?PROCNAME ! #offline_msg{user = LUser,
|
||||
timestamp = TimeStamp,
|
||||
from = From,
|
||||
to = To,
|
||||
packet = Packet};
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
@@ -125,11 +156,39 @@ 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) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
Rs = mnesia:read({offline_msg, LUser}),
|
||||
Rs = mnesia:wread({offline_msg, LUser}),
|
||||
mnesia:delete({offline_msg, LUser}),
|
||||
Rs
|
||||
end,
|
||||
@@ -153,6 +212,63 @@ resend_offline_messages(User) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
pop_offline_messages(Ls, User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
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)));
|
||||
_ ->
|
||||
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,
|
||||
@@ -160,6 +276,7 @@ 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 ->
|
||||
@@ -175,3 +292,29 @@ remove_user(User) ->
|
||||
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, from, to, packet] ->
|
||||
?INFO_MSG("Converting offline_msg table from "
|
||||
"{user, timestamp, from, to, packet} format", []),
|
||||
mnesia:transform_table(
|
||||
offline_msg,
|
||||
fun({_, U, TS, F, T, P}) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = P,
|
||||
Expire = find_x_expire(TS, Els),
|
||||
#offline_msg{user = U,
|
||||
timestamp = TS,
|
||||
expire = Expire,
|
||||
from = F,
|
||||
to = T,
|
||||
packet = P}
|
||||
end, Fields);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating offline_msg table", []),
|
||||
mnesia:transform_table(last_activity, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
+18
-25
@@ -12,7 +12,7 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1,
|
||||
-export([start/1, stop/0,
|
||||
process_iq/3,
|
||||
process_iq_set/3,
|
||||
process_iq_get/4,
|
||||
@@ -49,18 +49,11 @@ start(Opts) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_PRIVACY,
|
||||
?MODULE, process_iq, IQDisc).
|
||||
|
||||
%process_local_iq(From, To, {iq, _, Type, _, _} = IQ) ->
|
||||
% case Type of
|
||||
% set ->
|
||||
% process_iq_set(From, To, IQ);
|
||||
% get ->
|
||||
% process_iq_get(From, To, IQ)
|
||||
% end.
|
||||
%
|
||||
%
|
||||
%
|
||||
process_iq(From, To, IQ) ->
|
||||
{iq, ID, Type, XMLNS, SubEl} = IQ,
|
||||
stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_PRIVACY).
|
||||
|
||||
process_iq(From, _To, IQ) ->
|
||||
#iq{type = Type, sub_el = SubEl} = IQ,
|
||||
#jid{lserver = Server} = From,
|
||||
Res =
|
||||
case ?MYNAME of
|
||||
@@ -77,20 +70,20 @@ process_iq(From, To, IQ) ->
|
||||
end,
|
||||
case Res of
|
||||
{result, IQRes} ->
|
||||
{iq, ID, result, XMLNS, IQRes};
|
||||
IQ#iq{type = result, sub_el = IQRes};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS, [SubEl, Error]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
end.
|
||||
|
||||
|
||||
process_iq_get(From, To, {iq, ID, Type, XMLNS, SubEl},
|
||||
process_iq_get(From, _To, #iq{sub_el = SubEl},
|
||||
#userlist{name = Active}) ->
|
||||
#jid{luser = LUser} = From,
|
||||
{xmlelement, _, _, Els} = SubEl,
|
||||
case xml:remove_cdata(Els) of
|
||||
[] ->
|
||||
process_lists_get(LUser, Active);
|
||||
[{xmlelement, Name, Attrs, SubEls}] ->
|
||||
[{xmlelement, Name, Attrs, _SubEls}] ->
|
||||
case Name of
|
||||
"list" ->
|
||||
ListName = xml:get_attr("name", Attrs),
|
||||
@@ -105,7 +98,7 @@ process_iq_get(From, To, {iq, ID, Type, XMLNS, SubEl},
|
||||
|
||||
process_lists_get(LUser, Active) ->
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
{'EXIT', Reason} ->
|
||||
{'EXIT', _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
[] ->
|
||||
{result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
|
||||
@@ -144,7 +137,7 @@ process_lists_get(LUser, Active) ->
|
||||
|
||||
process_list_get(LUser, {value, Name}) ->
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
{'EXIT', Reason} ->
|
||||
{'EXIT', _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
[] ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
@@ -248,7 +241,7 @@ list_to_action(S) ->
|
||||
|
||||
|
||||
|
||||
process_iq_set(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_iq_set(From, _To, #iq{sub_el = SubEl}) ->
|
||||
#jid{luser = LUser} = From,
|
||||
{xmlelement, _, _, Els} = SubEl,
|
||||
case xml:remove_cdata(Els) of
|
||||
@@ -318,7 +311,7 @@ process_active_set(LUser, {value, Name}) ->
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
[] ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
[#privacy{lists = Lists} = P] ->
|
||||
[#privacy{lists = Lists}] ->
|
||||
case lists:keysearch(Name, 1, Lists) of
|
||||
{value, {_, List}} ->
|
||||
{result, [], #userlist{name = Name, list = List}};
|
||||
@@ -327,7 +320,7 @@ process_active_set(LUser, {value, Name}) ->
|
||||
end
|
||||
end;
|
||||
|
||||
process_active_set(LUser, false) ->
|
||||
process_active_set(_LUser, false) ->
|
||||
{result, [], #userlist{}}.
|
||||
|
||||
|
||||
@@ -490,7 +483,7 @@ parse_items([{xmlelement, "item", Attrs, SubEls} | Els], Res) ->
|
||||
false
|
||||
end;
|
||||
|
||||
parse_items(_, Res) ->
|
||||
parse_items(_, _Res) ->
|
||||
false.
|
||||
|
||||
|
||||
@@ -509,7 +502,7 @@ parse_matches1(Item, [{xmlelement, "presence-in", _, _} | Els]) ->
|
||||
parse_matches1(Item#listitem{match_presence_in = true}, Els);
|
||||
parse_matches1(Item, [{xmlelement, "presence-out", _, _} | Els]) ->
|
||||
parse_matches1(Item#listitem{match_presence_out = true}, Els);
|
||||
parse_matches1(Item, [{xmlelement, _, _, _} | Els]) ->
|
||||
parse_matches1(_Item, [{xmlelement, _, _, _} | _Els]) ->
|
||||
false.
|
||||
|
||||
|
||||
@@ -584,7 +577,7 @@ check_packet(User,
|
||||
end
|
||||
end.
|
||||
|
||||
check_packet_aux([], PType, JID, Subscription, Groups) ->
|
||||
check_packet_aux([], _PType, _JID, _Subscription, _Groups) ->
|
||||
allow;
|
||||
check_packet_aux([Item | List], PType, JID, Subscription, Groups) ->
|
||||
#listitem{type = Type, value = Value, action = Action} = Item,
|
||||
|
||||
+17
-15
@@ -14,7 +14,7 @@
|
||||
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
process_local_iq/3,
|
||||
process_sm_iq/3,
|
||||
remove_user/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -28,16 +28,16 @@ start(Opts) ->
|
||||
[{disc_only_copies, [node()]},
|
||||
{attributes, record_info(fields, private_storage)}]),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_PRIVATE,
|
||||
?MODULE, process_local_iq, IQDisc).
|
||||
?MODULE, process_sm_iq, IQDisc).
|
||||
|
||||
stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_PRIVATE).
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_PRIVATE).
|
||||
|
||||
|
||||
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_sm_iq(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
#jid{luser = LUser, lserver = LServer} = From,
|
||||
case ?MYNAME of
|
||||
Server ->
|
||||
LServer ->
|
||||
{xmlelement, Name, Attrs, Els} = SubEl,
|
||||
case Type of
|
||||
set ->
|
||||
@@ -48,24 +48,26 @@ process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
end, Els)
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
{iq, ID, result, XMLNS, [{xmlelement, Name, Attrs, []}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, Name, Attrs, []}]};
|
||||
get ->
|
||||
case catch get_data(LUser, Els) of
|
||||
{'EXIT', Reason} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
|
||||
{'EXIT', _Reason} ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl,
|
||||
?ERR_INTERNAL_SERVER_ERROR]};
|
||||
Res ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, Name, Attrs, Res}]}
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, Name, Attrs, Res}]}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end.
|
||||
|
||||
set_data(LUser, El) ->
|
||||
case El of
|
||||
{xmlelement, Name, Attrs, Els} ->
|
||||
{xmlelement, _Name, Attrs, _Els} ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs),
|
||||
case XMLNS of
|
||||
"" ->
|
||||
@@ -81,11 +83,11 @@ set_data(LUser, El) ->
|
||||
get_data(LUser, Els) ->
|
||||
get_data(LUser, Els, []).
|
||||
|
||||
get_data(LUser, [], Res) ->
|
||||
get_data(_LUser, [], Res) ->
|
||||
lists:reverse(Res);
|
||||
get_data(LUser, [El | Els], Res) ->
|
||||
case El of
|
||||
{xmlelement, Name, Attrs, _} ->
|
||||
{xmlelement, _Name, Attrs, _} ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs),
|
||||
case mnesia:dirty_read(private_storage, {LUser, XMLNS}) of
|
||||
[R] ->
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@
|
||||
LIBS = @LIBS@ @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
@@ -23,10 +18,12 @@ all: $(OBJS)
|
||||
$(OUTDIR)/%.beam: %.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ OBJS = \
|
||||
ALL : $(OBJS)
|
||||
|
||||
CLEAN :
|
||||
-@erase *.beam
|
||||
-@erase $(OBJS)
|
||||
|
||||
$(OUTDIR)\mod_pubsub.beam : mod_pubsub.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) mod_pubsub.erl
|
||||
|
||||
@@ -92,50 +92,52 @@ do_route(Host, From, To, Packet) ->
|
||||
case Name of
|
||||
"iq" ->
|
||||
case jlib:iq_query_info(Packet) of
|
||||
{iq, ID, get, ?NS_DISCO_INFO = XMLNS, SubEl} ->
|
||||
#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
|
||||
sub_el = SubEl} = IQ ->
|
||||
{xmlelement, _, QAttrs, _} = SubEl,
|
||||
Node = xml:get_attr_s("node", QAttrs),
|
||||
Res = {iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
QAttrs,
|
||||
iq_disco_info(Node)}]},
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
QAttrs,
|
||||
iq_disco_info(Node)}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
{iq, ID, get, ?NS_DISCO_ITEMS = XMLNS, SubEl} ->
|
||||
#iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS,
|
||||
sub_el = SubEl} = IQ ->
|
||||
{xmlelement, _, QAttrs, _} = SubEl,
|
||||
Node = xml:get_attr_s("node", QAttrs),
|
||||
Res =
|
||||
case iq_disco_items(Host, From, Node) of
|
||||
{result, IQRes} ->
|
||||
jlib:iq_to_xml(
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
QAttrs,
|
||||
IQRes}]});
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
QAttrs,
|
||||
IQRes}]});
|
||||
{error, Error} ->
|
||||
jlib:make_error_reply(
|
||||
Packet, Error)
|
||||
end,
|
||||
ejabberd_router:route(To, From, Res);
|
||||
%{iq, ID, get, ?NS_REGISTER = XMLNS, SubEl} ->
|
||||
% Lang = xml:get_tag_attr_s(
|
||||
% "xml:lang", SubEl),
|
||||
% Res = {iq, ID, result, XMLNS,
|
||||
% [{xmlelement, "query",
|
||||
% [{"xmlns", XMLNS}],
|
||||
% iq_get_register_info(
|
||||
% From, Lang)}]},
|
||||
%#iq{type = get, xmlns = ?NS_REGISTER = XMLNS,
|
||||
% lang = Lang, sub_el = SubEl} = IQ ->
|
||||
% Res = IQ#iq{type = result,
|
||||
% sub_el = [{xmlelement, "query",
|
||||
% [{"xmlns", XMLNS}],
|
||||
% iq_get_register_info(
|
||||
% From, Lang)}]},
|
||||
% ejabberd_router:route(To,
|
||||
% From,
|
||||
% jlib:iq_to_xml(Res));
|
||||
%{iq, ID, set, ?NS_REGISTER = XMLNS, SubEl} ->
|
||||
%#iq{type = set, xmlns = ?NS_REGISTER = XMLNS,
|
||||
% sub_el = SubEl} = IQ ->
|
||||
% case process_iq_register_set(From, SubEl) of
|
||||
% {result, IQRes} ->
|
||||
% Res = {iq, ID, result, XMLNS,
|
||||
% [{xmlelement, "query",
|
||||
% [{"xmlns", XMLNS}],
|
||||
% IQRes}]},
|
||||
% Res = IQ#iq{type = result,
|
||||
% sub_el = [{xmlelement, "query",
|
||||
% [{"xmlns", XMLNS}],
|
||||
% IQRes}]},
|
||||
% ejabberd_router:route(
|
||||
% To, From, jlib:iq_to_xml(Res));
|
||||
% {error, Error} ->
|
||||
@@ -144,35 +146,35 @@ do_route(Host, From, To, Packet) ->
|
||||
% ejabberd_router:route(
|
||||
% To, From, Err)
|
||||
% end;
|
||||
{iq, ID, Type, ?NS_PUBSUB = XMLNS, SubEl} ->
|
||||
#iq{type = Type, xmlns = ?NS_PUBSUB = XMLNS,
|
||||
sub_el = SubEl} = IQ ->
|
||||
Res =
|
||||
case iq_pubsub(Host, From, Type, SubEl) of
|
||||
{result, IQRes} ->
|
||||
jlib:iq_to_xml(
|
||||
{iq, ID, result, XMLNS,
|
||||
IQRes});
|
||||
IQ#iq{type = result,
|
||||
sub_el = IQRes});
|
||||
{error, Error} ->
|
||||
jlib:make_error_reply(
|
||||
Packet, Error)
|
||||
end,
|
||||
ejabberd_router:route(To, From, Res);
|
||||
{iq, ID, get, ?NS_VCARD = XMLNS, SubEl} ->
|
||||
Lang = xml:get_tag_attr_s(
|
||||
"xml:lang", SubEl),
|
||||
Res = {iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_vcard(Lang)}]},
|
||||
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
||||
lang = Lang, sub_el = SubEl} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "vCard",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_vcard(Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
reply ->
|
||||
ok;
|
||||
_ ->
|
||||
#iq{} ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet,
|
||||
?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
ejabberd_router:route(To, From, Err);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
@@ -209,8 +211,8 @@ iq_disco_info(SNode) ->
|
||||
case Node of
|
||||
[] ->
|
||||
[{xmlelement, "identity",
|
||||
[{"category", "service"},
|
||||
{"type", "pubsub"},
|
||||
[{"category", "pubsub"},
|
||||
{"type", "generic"},
|
||||
{"name", "ejabberd/mod_pubsub"}], []},
|
||||
%{xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
|
||||
{xmlelement, "feature", [{"var", ?NS_PUBSUB}], []},
|
||||
@@ -385,8 +387,10 @@ iq_get_vcard(Lang) ->
|
||||
[{xmlcdata,
|
||||
"http://ejabberd.jabberstudio.org/"}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata, "ejabberd pub/sub module\n"
|
||||
"Copyright (c) 2003 Alexey Shchepin"}]}].
|
||||
[{xmlcdata, translate:translate(
|
||||
Lang,
|
||||
"ejabberd pub/sub module\n"
|
||||
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||
|
||||
|
||||
iq_pubsub(Host, From, Type, SubEl) ->
|
||||
@@ -448,6 +452,8 @@ 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
|
||||
[] ->
|
||||
@@ -863,7 +869,7 @@ set_entities(OJID, Node, EntitiesEls) ->
|
||||
(Subscription == false) ->
|
||||
error;
|
||||
true ->
|
||||
[{JID,
|
||||
[{jlib:jid_tolower(JID),
|
||||
#entity{
|
||||
affiliation = Affiliation,
|
||||
subscription = Subscription}} |
|
||||
@@ -980,7 +986,7 @@ subscription_to_string(Subscription) ->
|
||||
|
||||
check_create_permission(Host, Node, Owner) ->
|
||||
if
|
||||
#jid{lserver = Host} == Owner ->
|
||||
Owner#jid.lserver == Host ->
|
||||
true;
|
||||
true ->
|
||||
#jid{luser = User, lserver = Server} = Owner,
|
||||
|
||||
+43
-37
@@ -12,7 +12,7 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1, init/0, process_iq/3]).
|
||||
-export([start/1, stop/0, process_iq/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
@@ -25,10 +25,11 @@ start(Opts) ->
|
||||
?MODULE, process_iq, IQDisc),
|
||||
ok.
|
||||
|
||||
init() ->
|
||||
ok.
|
||||
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, ID, Type, XMLNS, SubEl}) ->
|
||||
process_iq(From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
UTag = xml:get_subtag(SubEl, "username"),
|
||||
@@ -41,7 +42,7 @@ process_iq(From, _To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
case From of
|
||||
#jid{user = User, lserver = Server} ->
|
||||
ejabberd_auth:remove_user(User),
|
||||
{iq, ID, result, XMLNS, [SubEl]};
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
_ ->
|
||||
if
|
||||
PTag /= false ->
|
||||
@@ -49,34 +50,35 @@ process_iq(From, _To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
case ejabberd_auth:remove_user(User,
|
||||
Password) of
|
||||
ok ->
|
||||
{iq, ID, result, XMLNS, [SubEl]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [SubEl]};
|
||||
not_allowed ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error,
|
||||
sub_el =
|
||||
[SubEl, ?ERR_NOT_ALLOWED]};
|
||||
not_exists ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, {xmlelement,
|
||||
"error",
|
||||
[{"code", "404"}],
|
||||
[{xmlcdata,
|
||||
"Not Found"}]}]};
|
||||
IQ#iq{type = error,
|
||||
sub_el =
|
||||
[SubEl, ?ERR_ITEM_NOT_FOUND]};
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl,
|
||||
?ERR_INTERNAL_SERVER_ERROR]}
|
||||
IQ#iq{type = error,
|
||||
sub_el =
|
||||
[SubEl,
|
||||
?ERR_INTERNAL_SERVER_ERROR]}
|
||||
end;
|
||||
true ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_BAD_REQUEST]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_BAD_REQUEST]}
|
||||
end
|
||||
end;
|
||||
(UTag == false) and (RTag /= false) ->
|
||||
case From of
|
||||
#jid{user = User, lserver = Server} ->
|
||||
ejabberd_auth:remove_user(User),
|
||||
{iq, ID, result, XMLNS, [SubEl]};
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end;
|
||||
(UTag /= false) and (PTag /= false) ->
|
||||
User = xml:get_tag_cdata(UTag),
|
||||
@@ -84,30 +86,33 @@ process_iq(From, _To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
case From of
|
||||
#jid{user = User, lserver = Server} ->
|
||||
ejabberd_auth:set_password(User, Password),
|
||||
{iq, ID, result, XMLNS, [SubEl]};
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
_ ->
|
||||
case try_register(User, Password) of
|
||||
ok ->
|
||||
{iq, ID, result, XMLNS, [SubEl]};
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, Error]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, Error]}
|
||||
end
|
||||
end;
|
||||
true ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_BAD_REQUEST]}
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_BAD_REQUEST]}
|
||||
end;
|
||||
get ->
|
||||
{iq, ID, result, XMLNS, [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", "jabber:iq:register"}],
|
||||
[{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
"Choose a username and password "
|
||||
"to register with this server."}]},
|
||||
{xmlelement, "username", [], []},
|
||||
{xmlelement, "password", [], []}]}]}
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", "jabber:iq:register"}],
|
||||
[{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang,
|
||||
"Choose a username and password "
|
||||
"to register with this server")}]},
|
||||
{xmlelement, "username", [], []},
|
||||
{xmlelement, "password", [], []}]}]}
|
||||
end.
|
||||
|
||||
|
||||
@@ -117,7 +122,8 @@ try_register(User, Password) ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
JID = jlib:make_jid(User, ?MYNAME, ""),
|
||||
case acl:match_rule(register, JID) of
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, all),
|
||||
case acl:match_rule(Access, JID) of
|
||||
deny ->
|
||||
{error, ?ERR_CONFLICT};
|
||||
allow ->
|
||||
|
||||
+201
-203
@@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_roster.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Purpose : Roster management
|
||||
%%% Created : 11 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1,
|
||||
-export([start/1, stop/0,
|
||||
process_iq/3,
|
||||
process_local_iq/3,
|
||||
get_subscription_lists/1,
|
||||
@@ -22,7 +22,6 @@
|
||||
remove_user/1,
|
||||
get_jid_info/2]).
|
||||
|
||||
-include_lib("mnemosyne/include/mnemosyne.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
@@ -44,12 +43,16 @@ start(Opts) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_ROSTER,
|
||||
?MODULE, process_iq, IQDisc).
|
||||
|
||||
stop() ->
|
||||
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, ID, _Type, XMLNS, SubEl} = IQ,
|
||||
#iq{sub_el = SubEl} = IQ,
|
||||
#jid{lserver = LServer} = From,
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
@@ -58,26 +61,24 @@ process_iq(From, To, IQ) ->
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
ignore;
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
end.
|
||||
|
||||
-else.
|
||||
|
||||
process_iq(From, To, IQ) ->
|
||||
{iq, ID, _Type, XMLNS, SubEl} = IQ,
|
||||
#iq{sub_el = SubEl} = IQ,
|
||||
#jid{lserver = LServer} = From,
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
process_local_iq(From, To, IQ);
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
end.
|
||||
|
||||
-endif.
|
||||
|
||||
process_local_iq(From, To, {iq, _, Type, _, _} = IQ) ->
|
||||
process_local_iq(From, To, #iq{type = Type} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
process_iq_set(From, To, IQ);
|
||||
@@ -87,20 +88,17 @@ process_local_iq(From, To, {iq, _, Type, _, _} = IQ) ->
|
||||
|
||||
|
||||
|
||||
process_iq_get(From, _To, {iq, ID, _Type, XMLNS, SubEl}) ->
|
||||
process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) ->
|
||||
#jid{luser = LUser} = From,
|
||||
F = fun() ->
|
||||
mnesia:index_read(roster, LUser, #roster.user)
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, Items} ->
|
||||
case catch mnesia:dirty_index_read(roster, LUser, #roster.user) of
|
||||
Items when is_list(Items) ->
|
||||
XItems = lists:map(fun item_to_xml/1, Items),
|
||||
{iq, ID, result, XMLNS, [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
XItems}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
XItems}]};
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
|
||||
end.
|
||||
|
||||
item_to_xml(Item) ->
|
||||
@@ -123,13 +121,13 @@ item_to_xml(Item) ->
|
||||
remove ->
|
||||
[{"subscription", "remove"} | Attrs2]
|
||||
end,
|
||||
Attrs4 = case Item#roster.ask of
|
||||
none ->
|
||||
Attrs3;
|
||||
subscribe ->
|
||||
Attrs4 = case ask_to_pending(Item#roster.ask) of
|
||||
out ->
|
||||
[{"ask", "subscribe"} | Attrs3];
|
||||
unsubscribe ->
|
||||
[{"ask", "unsubscribe"} | Attrs3]
|
||||
both ->
|
||||
[{"ask", "subscribe"} | Attrs3];
|
||||
_ ->
|
||||
Attrs3
|
||||
end,
|
||||
Attrs = Attrs4 ++ Item#roster.xattrs,
|
||||
SubEls1 = lists:map(fun(G) ->
|
||||
@@ -139,10 +137,10 @@ item_to_xml(Item) ->
|
||||
{xmlelement, "item", Attrs, SubEls}.
|
||||
|
||||
|
||||
process_iq_set(From, To, {iq, ID, _Type, XMLNS, SubEl}) ->
|
||||
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, ID, result, XMLNS, []}.
|
||||
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)),
|
||||
@@ -195,7 +193,8 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
end,
|
||||
if IsTo ->
|
||||
ejabberd_router:route(
|
||||
From, jlib:make_jid(OldItem#roster.jid),
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(OldItem#roster.jid),
|
||||
{xmlelement, "presence",
|
||||
[{"type", "unsubscribe"}],
|
||||
[]});
|
||||
@@ -203,7 +202,8 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
end,
|
||||
if IsFrom ->
|
||||
ejabberd_router:route(
|
||||
From, jlib:make_jid(OldItem#roster.jid),
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(OldItem#roster.jid),
|
||||
{xmlelement, "presence",
|
||||
[{"type", "unsubscribed"}],
|
||||
[]});
|
||||
@@ -287,37 +287,34 @@ push_item(User, From, Item) ->
|
||||
% TODO: don't push to those who not load roster
|
||||
-ifdef(PSI_ROSTER_WORKAROUND).
|
||||
|
||||
push_item(User, Resource, From, Item) ->
|
||||
ResIQ = {iq, "", set, ?NS_ROSTER,
|
||||
[{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)}.
|
||||
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, "", set, ?NS_ROSTER,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[item_to_xml(Item)]}]},
|
||||
ejabberd_router ! {route,
|
||||
From,
|
||||
jlib:make_jid(User, ?MYNAME, Resource),
|
||||
jlib:iq_to_xml(ResIQ)}.
|
||||
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),
|
||||
F = fun() ->
|
||||
mnesia:index_read(roster, LUser, #roster.user)
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, Items} ->
|
||||
case mnesia:dirty_index_read(roster, LUser, #roster.user) of
|
||||
Items when is_list(Items) ->
|
||||
fill_subscription_lists(Items, [], []);
|
||||
_ ->
|
||||
{[], []}
|
||||
@@ -338,176 +335,177 @@ fill_subscription_lists([I | Is], F, T) ->
|
||||
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, From, Type) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
F = fun() ->
|
||||
case mnesia:read({roster, {LUser, LFrom}}) of
|
||||
[] ->
|
||||
case Type of
|
||||
subscribe ->
|
||||
true;
|
||||
unsubscribe ->
|
||||
true;
|
||||
unsubscribed ->
|
||||
false;
|
||||
subscribed ->
|
||||
JID = {From#jid.user,
|
||||
From#jid.server,
|
||||
From#jid.resource},
|
||||
NewItem = #roster{uj = {LUser, LFrom},
|
||||
user = LUser,
|
||||
jid = JID},
|
||||
mnesia:write(NewItem),
|
||||
true
|
||||
end;
|
||||
[I] ->
|
||||
case Type of
|
||||
subscribe ->
|
||||
S = I#roster.subscription,
|
||||
if
|
||||
(S == both) or (S == from) ->
|
||||
{update,
|
||||
{xmlelement, "presence",
|
||||
[{"type", "subscribed"}], []},
|
||||
I};
|
||||
true ->
|
||||
true
|
||||
end;
|
||||
unsubscribe ->
|
||||
S = I#roster.subscription,
|
||||
if
|
||||
(S == none) or (S == to) ->
|
||||
{update,
|
||||
{xmlelement, "presence",
|
||||
[{"type", "unsubscribed"}], []},
|
||||
I};
|
||||
true ->
|
||||
true
|
||||
end;
|
||||
_ ->
|
||||
S = I#roster.subscription,
|
||||
NS = case Type of
|
||||
subscribed ->
|
||||
case S of
|
||||
from -> both;
|
||||
none -> to;
|
||||
_ -> S
|
||||
end;
|
||||
unsubscribed ->
|
||||
case S of
|
||||
both -> from;
|
||||
to -> none;
|
||||
_ -> S
|
||||
end
|
||||
end,
|
||||
NewItem = I#roster{subscription = NS,
|
||||
ask = none},
|
||||
mnesia:write(NewItem),
|
||||
{push, NewItem}
|
||||
end
|
||||
end
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, true} ->
|
||||
true;
|
||||
{atomic, false} ->
|
||||
false;
|
||||
{atomic, {update, Presence, Item}} ->
|
||||
ejabberd_router:route(jlib:make_jid(User, ?MYNAME, ""),
|
||||
jlib:jid_replace_resource(From, ""),
|
||||
Presence),
|
||||
ejabberd_sm ! {route,
|
||||
jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, "", ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{item,
|
||||
Item#roster.jid,
|
||||
Item#roster.subscription}]}},
|
||||
false;
|
||||
{atomic, {push, Item}} ->
|
||||
push_item(User, {"", ?MYNAME, ""}, Item),
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
out_subscription(User, JID1, Type) ->
|
||||
|
||||
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),
|
||||
F = fun() ->
|
||||
Item = case mnesia:read({roster, {LUser, LJID}}) of
|
||||
[] ->
|
||||
if (Type == unsubscribe) or
|
||||
(Type == unsubscribed) ->
|
||||
false;
|
||||
true ->
|
||||
JID = {JID1#jid.user,
|
||||
JID1#jid.server,
|
||||
JID1#jid.resource},
|
||||
#roster{uj = {LUser, LJID},
|
||||
user = LUser,
|
||||
jid = JID}
|
||||
end;
|
||||
JID = {JID1#jid.user,
|
||||
JID1#jid.server,
|
||||
JID1#jid.resource},
|
||||
#roster{uj = {LUser, LJID},
|
||||
user = LUser,
|
||||
jid = JID};
|
||||
[I] ->
|
||||
I
|
||||
end,
|
||||
if Item == false ->
|
||||
ok;
|
||||
true ->
|
||||
{NewItem, Update} =
|
||||
case Type of
|
||||
subscribe ->
|
||||
{Item#roster{ask = subscribe}, false};
|
||||
unsubscribe ->
|
||||
{Item#roster{ask = unsubscribe}, false};
|
||||
subscribed ->
|
||||
S = Item#roster.subscription,
|
||||
NS = case S of
|
||||
to -> both;
|
||||
none -> from;
|
||||
_ -> S
|
||||
end,
|
||||
{Item#roster{subscription = NS,
|
||||
ask = none},
|
||||
true};
|
||||
unsubscribed ->
|
||||
S = Item#roster.subscription,
|
||||
NS = case S of
|
||||
both -> to;
|
||||
from -> none;
|
||||
_ -> S
|
||||
end,
|
||||
{Item#roster{subscription = NS,
|
||||
ask = none},
|
||||
true}
|
||||
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,
|
||||
case NewState of
|
||||
none ->
|
||||
{none, AutoReply};
|
||||
{Subscription, Pending} ->
|
||||
NewItem = Item#roster{subscription = Subscription,
|
||||
ask = Pending},
|
||||
mnesia:write(NewItem),
|
||||
{push, NewItem, Update}
|
||||
{{push, NewItem}, AutoReply}
|
||||
end
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
{atomic, {push, Item, Update}} ->
|
||||
push_item(User, {"", ?MYNAME, ""}, Item),
|
||||
if
|
||||
Update ->
|
||||
ejabberd_sm ! {route,
|
||||
jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, "", ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{item,
|
||||
Item#roster.jid,
|
||||
Item#roster.subscription}]}};
|
||||
true ->
|
||||
ok
|
||||
{atomic, {Push, AutoReply}} ->
|
||||
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, Item} ->
|
||||
push_item(User, jlib:make_jid("", ?MYNAME, ""), Item),
|
||||
true;
|
||||
none ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
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),
|
||||
F = fun() ->
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% 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).
|
||||
|
||||
+9
-7
@@ -27,11 +27,12 @@ stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_STATS).
|
||||
|
||||
|
||||
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_local_iq(From, To, #iq{id = ID, type = Type,
|
||||
xmlns = XMLNS, sub_el = SubEl} = IQ) ->
|
||||
Lang = xml:get_tag_attr_s("xml:lang", SubEl),
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
{xmlelement, _, Attrs, Els} = SubEl,
|
||||
Node = string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||
@@ -39,11 +40,12 @@ process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
|
||||
case get_local_stats(Node, Names) of
|
||||
{result, Res} ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query", [{"xmlns", XMLNS}], Res}]};
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
Res}]};
|
||||
{error, Error} ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, Error]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -87,7 +89,7 @@ get_local_stats(["running nodes", _], []) ->
|
||||
get_local_stats(["running nodes", ENode], Names) ->
|
||||
case search_running_node(ENode) of
|
||||
false ->
|
||||
{error, "404", "Not Found"};
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
Node ->
|
||||
{result,
|
||||
lists:map(fun(Name) -> get_node_stat(Node, Name) end, Names)}
|
||||
|
||||
+7
-9
@@ -28,19 +28,17 @@ start(Opts) ->
|
||||
stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_TIME).
|
||||
|
||||
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
UTC = jlib:timestamp_to_iso(calendar:universal_time()),
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_TIME}],
|
||||
[{xmlelement, "utc", [],
|
||||
[{xmlcdata, UTC}]}
|
||||
]}]}
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_TIME}],
|
||||
[{xmlelement, "utc", [],
|
||||
[{xmlcdata, UTC}]}]}]}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
+174
-87
@@ -12,7 +12,7 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1, init/1,
|
||||
-export([start/1, init/2, stop/0,
|
||||
process_local_iq/3,
|
||||
process_sm_iq/3,
|
||||
reindex_vcards/0,
|
||||
@@ -22,7 +22,8 @@
|
||||
-include("jlib.hrl").
|
||||
|
||||
|
||||
%-define(JUD_ALLOW_RETURN_ALL, true)
|
||||
%-define(JUD_ALLOW_RETURN_ALL, true).
|
||||
-define(JUD_MATCHES, 30).
|
||||
|
||||
-record(vcard_search, {user, luser,
|
||||
fn, lfn,
|
||||
@@ -63,15 +64,22 @@ start(Opts) ->
|
||||
?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),
|
||||
Host = gen_mod:get_opt(host, Opts, "vjud." ++ ?MYNAME),
|
||||
spawn(?MODULE, init, [Host]).
|
||||
Search = gen_mod:get_opt(search, Opts, true),
|
||||
register(ejabberd_mod_vcard, spawn(?MODULE, init, [Host, Search])).
|
||||
|
||||
|
||||
init(Host) ->
|
||||
ejabberd_router:register_local_route(Host),
|
||||
loop().
|
||||
init(Host, Search) ->
|
||||
case Search of
|
||||
false ->
|
||||
loop(Host);
|
||||
_ ->
|
||||
ejabberd_router:register_route(Host),
|
||||
loop(Host)
|
||||
end.
|
||||
|
||||
loop() ->
|
||||
loop(Host) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
@@ -80,45 +88,56 @@ loop() ->
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop();
|
||||
loop(Host);
|
||||
stop ->
|
||||
catch ejabberd_router:unregister_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop()
|
||||
loop(Host)
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
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.
|
||||
|
||||
process_local_iq(_From, _To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "vCard",
|
||||
[{"xmlns", ?NS_VCARD}],
|
||||
[{xmlelement, "FN", [],
|
||||
[{xmlcdata, "ejabberd"}]},
|
||||
{xmlelement, "URL", [],
|
||||
[{xmlcdata,
|
||||
"http://ejabberd.jabberstudio.org/"}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata, "Erlang Jabber Server\n"
|
||||
"Copyright (c) 2002, 2003 Alexey Shchepin"}]},
|
||||
{xmlelement, "BDAY", [],
|
||||
[{xmlcdata, "2002-11-16"}]}
|
||||
]}]}
|
||||
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-2004 Alexey Shchepin")}]},
|
||||
{xmlelement, "BDAY", [],
|
||||
[{xmlcdata, "2002-11-16"}]}
|
||||
]}]}
|
||||
end.
|
||||
|
||||
|
||||
process_sm_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
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 ?MYNAME of
|
||||
LServer ->
|
||||
set_vcard(User, SubEl),
|
||||
{iq, ID, result, XMLNS, []};
|
||||
IQ#iq{type = result, sub_el = []};
|
||||
_ ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end;
|
||||
get ->
|
||||
#jid{luser = LUser} = To,
|
||||
@@ -133,7 +152,7 @@ process_sm_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
{aborted, _Reason} ->
|
||||
[]
|
||||
end,
|
||||
{iq, ID, result, XMLNS, Els}
|
||||
IQ#iq{type = result, sub_el = Els}
|
||||
end.
|
||||
|
||||
set_vcard(User, VCARD) ->
|
||||
@@ -145,9 +164,16 @@ set_vcard(User, VCARD) ->
|
||||
BDay = xml:get_path_s(VCARD, [{elem, "BDAY"}, cdata]),
|
||||
CTRY = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "CTRY"}, cdata]),
|
||||
Locality = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "LOCALITY"},cdata]),
|
||||
EMail = xml:get_path_s(VCARD, [{elem, "EMAIL"}, cdata]),
|
||||
EMail1 = xml:get_path_s(VCARD, [{elem, "EMAIL"}, {elem, "USERID"},cdata]),
|
||||
EMail2 = xml:get_path_s(VCARD, [{elem, "EMAIL"}, cdata]),
|
||||
OrgName = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGNAME"}, cdata]),
|
||||
OrgUnit = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGUNIT"}, cdata]),
|
||||
EMail = case EMail1 of
|
||||
"" ->
|
||||
EMail2;
|
||||
_ ->
|
||||
EMail1
|
||||
end,
|
||||
|
||||
LUser = jlib:nodeprep(User),
|
||||
LFN = stringprep:tolower(FN),
|
||||
@@ -203,15 +229,18 @@ set_vcard(User, VCARD) ->
|
||||
{"var", Var}], []}).
|
||||
|
||||
|
||||
-define(FORM,
|
||||
-define(FORM(JID),
|
||||
[{xmlelement, "instructions", [],
|
||||
[{xmlcdata, translate:translate(Lang, "You need a x:data capable client to search")}]},
|
||||
[{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, "Users Search")}]},
|
||||
[{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")}]},
|
||||
[{xmlcdata, translate:translate(Lang, "Fill in the form to search "
|
||||
"for any matching Jabber User "
|
||||
"(Add * to the end of field to "
|
||||
"match substring)")}]},
|
||||
?TLFIELD("text-single", "User", "user"),
|
||||
?TLFIELD("text-single", "Full Name", "fn"),
|
||||
?TLFIELD("text-single", "Name", "given"),
|
||||
@@ -238,8 +267,7 @@ do_route(From, To, Packet) ->
|
||||
true ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
{iq, ID, Type, ?NS_SEARCH, SubEl} ->
|
||||
Lang = xml:get_tag_attr_s("xml:lang", SubEl),
|
||||
#iq{type = Type, xmlns = ?NS_SEARCH, lang = Lang, sub_el = SubEl} ->
|
||||
case Type of
|
||||
set ->
|
||||
XDataEl = find_xdata_el(SubEl),
|
||||
@@ -259,68 +287,87 @@ do_route(From, To, Packet) ->
|
||||
Err);
|
||||
_ ->
|
||||
ResIQ =
|
||||
{iq, ID, result, ?NS_SEARCH,
|
||||
[{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", ?NS_SEARCH}],
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_XDATA},
|
||||
{"type", "result"}],
|
||||
search_result(Lang, XData)
|
||||
}]}]},
|
||||
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, ID, result, ?NS_SEARCH,
|
||||
[{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", ?NS_SEARCH}],
|
||||
?FORM
|
||||
}]},
|
||||
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, ID, Type, ?NS_DISCO_INFO, SubEl} ->
|
||||
#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, ID, result, ?NS_DISCO_INFO,
|
||||
[{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", ?NS_DISCO_INFO}],
|
||||
[{xmlelement, "identity",
|
||||
[{"category", "directory"},
|
||||
{"type", "user"},
|
||||
{"name", "vCard User Search"}], []},
|
||||
{xmlelement, "feature",
|
||||
[{"var", ?NS_SEARCH}], []}
|
||||
]
|
||||
}]},
|
||||
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, ID, Type, ?NS_DISCO_ITEMS, SubEl} ->
|
||||
#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, ID, result, ?NS_DISCO_INFO,
|
||||
[{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", ?NS_DISCO_INFO}], []}]},
|
||||
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),
|
||||
@@ -328,6 +375,18 @@ do_route(From, To, Packet) ->
|
||||
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-2004 Alexey Shchepin")}]}].
|
||||
|
||||
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||
find_xdata_el1(SubEls).
|
||||
|
||||
@@ -347,9 +406,10 @@ find_xdata_el1([_ | Els]) ->
|
||||
{xmlelement, "field", [{"label", translate:translate(Lang, Label)},
|
||||
{"var", Var}], []}).
|
||||
|
||||
search_result(Lang, Data) ->
|
||||
search_result(Lang, JID, Data) ->
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata, translate:translate(Lang, "Users Search Results")}]},
|
||||
[{xmlcdata, translate:translate(Lang, "Results of search in ") ++
|
||||
jlib:jid_to_string(JID)}]},
|
||||
{xmlelement, "reported", [],
|
||||
[?LFIELD("JID", "jid"),
|
||||
?LFIELD("Full Name", "fn"),
|
||||
@@ -397,7 +457,17 @@ search(Data) ->
|
||||
?ERROR_MSG("~p", [Reason]),
|
||||
[];
|
||||
Rs ->
|
||||
Rs
|
||||
case gen_mod:get_module_opt(?MODULE, matches, 30) 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
|
||||
end.
|
||||
|
||||
-else.
|
||||
@@ -414,7 +484,17 @@ search(Data) ->
|
||||
?ERROR_MSG("~p", [Reason]),
|
||||
[];
|
||||
Rs ->
|
||||
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
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -444,24 +524,31 @@ filter_fields([{SVar, [Val]} | Ds], Match)
|
||||
when is_list(Val) and (Val /= "") ->
|
||||
LVal = stringprep:tolower(Val),
|
||||
NewMatch = case SVar of
|
||||
"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};
|
||||
"user" -> Match#vcard_search{luser = make_val(LVal)};
|
||||
"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)};
|
||||
_ -> Match
|
||||
end,
|
||||
filter_fields(Ds, NewMatch);
|
||||
filter_fields([_ | Ds], Match) ->
|
||||
filter_fields(Ds, Match).
|
||||
|
||||
make_val(Val) ->
|
||||
case lists:suffix("*", Val) of
|
||||
true ->
|
||||
lists:sublist(Val, length(Val) - 1) ++ '_';
|
||||
_ ->
|
||||
Val
|
||||
end.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
@@ -0,0 +1,572 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% 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),
|
||||
eldap:start_link("mod_vcard_ldap", LDAPServers, 389, "", ""),
|
||||
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-2004 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-2004 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(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(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)) ];
|
||||
_ ->
|
||||
?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.
|
||||
|
||||
+13
-13
@@ -30,11 +30,11 @@ stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_VERSION).
|
||||
|
||||
|
||||
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
process_local_iq(From, To, #iq{id = ID, type = Type,
|
||||
xmlns = XMLNS, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]};
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
OSType = case os:type() of
|
||||
{Osfamily, Osname} ->
|
||||
@@ -52,16 +52,16 @@ process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
VersionString
|
||||
end,
|
||||
OS = OSType ++ " " ++ OSVersion,
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_VERSION}],
|
||||
[{xmlelement, "name", [],
|
||||
[{xmlcdata, "ejabberd"}]},
|
||||
{xmlelement, "version", [],
|
||||
[{xmlcdata, ?VERSION}]},
|
||||
{xmlelement, "os", [],
|
||||
[{xmlcdata, OS}]}
|
||||
]}]}
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_VERSION}],
|
||||
[{xmlelement, "name", [],
|
||||
[{xmlcdata, "ejabberd"}]},
|
||||
{xmlelement, "version", [],
|
||||
[{xmlcdata, ?VERSION}]},
|
||||
{xmlelement, "os", [],
|
||||
[{xmlcdata, OS}]}
|
||||
]}]}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
% $Id$
|
||||
|
||||
% Ejabberd - Spanish - badlop AT jabberes.org
|
||||
|
||||
% jlib.hrl
|
||||
{"No resource provided", ""}.
|
||||
{"Illegal resource format", ""}.
|
||||
{"Resource conflict", ""}.
|
||||
|
||||
% 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"}.
|
||||
{"RAM copy", "Copia en RAM"}.
|
||||
{"RAM and disc copy", "Copia en RAM y disco"}.
|
||||
{"Disc only copy", "Copia en disco solamente"}.
|
||||
{"Remote copy", "Copia remota"}.
|
||||
{"Stop Modules at ", "Detener módulos en "}.
|
||||
{"Choose modules to stop", "Selecciona módulos a detener"}.
|
||||
{"Start Modules at ", "Iniciar módulos en "}.
|
||||
{"Enter list of {Module, [Options]}", "Introduce lista de {módulo, [opciones]}"}.
|
||||
{"List of modules to start", "Lista de módulos a iniciar"}.
|
||||
{"Backup to File at ", "Guardar copia de seguridad en fichero en "}.
|
||||
{"Enter path to backup file", "Introduce ruta al fichero de copia de seguridad"}.
|
||||
{"Path to File", "Ruta al fichero"}.
|
||||
{"Restore Backup from File at ", "Restaura copia de seguridad desde el fichero en "}.
|
||||
{"Dump Backup to Text File at ", "Exporta copia de seguridad a fichero de texto en "}.
|
||||
{"Enter path to text file", "Introduce ruta al fichero de texto"}.
|
||||
{"Import User from File at ", "Importa usuario desde fichero en "}.
|
||||
{"Enter path to jabberd1.4 spool file", "Introduce ruta al fichero jabberd1.4 spool"}.
|
||||
{"Import Users from Dir at ", "Importar usuarios desde el directorio en "}.
|
||||
{"Enter path to jabberd1.4 spool dir", "Introduce la ruta al directorio de jabberd1.4 spools"}.
|
||||
{"Path to Dir", "Ruta al directorio"}.
|
||||
{"Hostname Configuration", "Configuración del nombre de máquina"}.
|
||||
{"Choose host name", "Indica el nombre de la máquina"}.
|
||||
{"Host name", "Nombre de la máquina"}.
|
||||
{"Access Control List Configuration", "Configuración de la Lista de Control de Acceso"}.
|
||||
{"Access control lists", "Listas de Control de Acceso"}.
|
||||
{"Access Configuration", "Configuración de accesos"}.
|
||||
{"Access rules", "Reglas de acceso"}.
|
||||
{"Remove Users", "Eliminar usuarios"}.
|
||||
{"Choose users to remove", "Selecciona usuarios a eliminar"}.
|
||||
{"Administration of ", "Administración de "}.
|
||||
{"Action on user", "Acción en el usuario"}.
|
||||
{"Edit Properties", "Editar propiedades"}.
|
||||
{"Remove User", "Eliminar usuario"}.
|
||||
|
||||
% mod_disco.erl
|
||||
{"Configuration", "Configuración"}.
|
||||
{"Online Users", "Usuarios conectados"}.
|
||||
{"All Users", "Todos los usuarios"}.
|
||||
{"Outgoing S2S connections", "Conexiones S2S salientes"}.
|
||||
{"To ~s", "A ~s"}.
|
||||
{"From ~s", "De ~s"}.
|
||||
{"Running Nodes", "Nodos funcionando"}.
|
||||
{"Stopped Nodes", "Nodos detenidos"}.
|
||||
{"Host Name", "Nombre de la máquina"}.
|
||||
{"Access Control Lists", "Listas de Control de Acceso"}.
|
||||
{"Access Rules", "Reglas de Acceso"}.
|
||||
{"Remove Users", "Eliminar usuarios"}.
|
||||
{"DB", "BD"}.
|
||||
{"Modules", "Módulos"}.
|
||||
{"Start Modules", "Iniciar módulos"}.
|
||||
{"Stop Modules", "Detener módulos"}.
|
||||
{"Backup Management", "Gestión de copia de seguridad"}.
|
||||
{"Import users from jabberd1.4 spool files", "Importar usuarios desde ficheros jabberd1.4 spool"}.
|
||||
{"Backup", "Guardar copia de seguridad"}.
|
||||
{"Restore", "Restaurar"}.
|
||||
{"Dump to Text File", "Exportar a fichero de texto"}.
|
||||
{"Import File", "Importar fichero"}.
|
||||
{"Import Directory", "Importar directorio"}.
|
||||
|
||||
% mod_register.erl
|
||||
{"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-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"}.
|
||||
{"Full Name", "Nombre completo"}.
|
||||
{"Name", "Nombre"}.
|
||||
{"Middle Name", "Segundo nombre"}.
|
||||
{"Family Name", "Apellido"}.
|
||||
{"Nickname", "Apodo"}.
|
||||
{"Birthday", "Cumpleaños"}.
|
||||
{"Country", "País"}.
|
||||
{"City", "Ciudad"}.
|
||||
{"email", "correo-e"}.
|
||||
{"Organization Name", "Nombre de la organización"}.
|
||||
{"Organization Unit", "Unidad de la organización"}.
|
||||
|
||||
% mod_pubsub/mod_pubsub.erl
|
||||
{"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"}.
|
||||
|
||||
% mod_muc/mod_muc_room.erl
|
||||
{" has set the subject to: ", " ha puesto el asunto: "}.
|
||||
{"You need an x:data capable client to configure room", "Necesitas un cliente con soporte de x:data para configurar la sala"}.
|
||||
{"Configuration for ", "Configuración para "}.
|
||||
{"Room title", "Título de la sala"}.
|
||||
{"Allow users to change subject?", "Permitir a los usuarios cambiar el asunto"}.
|
||||
{"Allow users to query other users?", "Permitir a los usuarios consultar a otros usuarios"}.
|
||||
{"Allow users to send private messages?", "Permitir a los usuarios enviar mensajes privados"}.
|
||||
{"Make room public searchable?", "Hacer la sala públicamente visible"}.
|
||||
{"Make participants list public?", "Hacer pública la lista de participantes"}.
|
||||
{"Make room persistent?", "Hacer la sala permanente"}.
|
||||
{"Make room moderated?", "Hacer la sala moderada"}.
|
||||
{"Default users as members?", "Los usuarios son miembros por defecto"}.
|
||||
{"Make room members only?", "Hacer la sala sólo para miembros"}.
|
||||
{"Allow users to send invites?", "Permitir a los usuarios enviar invitaciones"}.
|
||||
{"Make room password protected?", "Proteger la sala con contraseña"}.
|
||||
{"Password", "Contraseña"}.
|
||||
{"Make room anonymous?", "Hacer anónima la sala"}.
|
||||
{"Enable logging?", "Guardar históricos"}.
|
||||
{"Only moderators and participants are allowed to change subject in this room", "Solo los moderadores y participantes pueden cambiar el asunto de esta sala"}.
|
||||
{"Only moderators are allowed to change subject in this room", "Solo los moderadores pueden cambiar el asunto de esta sala"}.
|
||||
{"Visitors are not allowed to send messages to all occupants", "Los visitantes no pueden enviar mensajes a todos los ocupantes"}.
|
||||
{"Only occupants are allowed to send messages to the conference", "Solo los ocupantes pueden enviar mensajes a la sala"}.
|
||||
{"It is not allowed to send normal messages to the conference", "Impedir el envio de mensajes normales a la sala"}.
|
||||
{"It is not allowed to send private messages to the conference", "Impedir el envio de mensajes privados a la sala"}.
|
||||
{"Improper message type", "Tipo de mensaje incorrecto"}.
|
||||
{"Nickname is already in use by another occupant", "El apodo ya está siendo usado por otro ocupante"}.
|
||||
{"Nickname is registered by another person", "El apodo ya está registrado por otra persona"}.
|
||||
{"It is not allowed to send private messages of type \"groupchat\"", "No está permitido enviar mensajes privados del tipo \"groupchat\""}.
|
||||
{"Recipient is not in the conference room", "El receptor no está en la sala de conferencia"}.
|
||||
{"Only occupants are allowed to send queries to the conference", "Solo los ocupantes pueden enviar solicitudes a la sala"}.
|
||||
{"Queries to the conference members are not allowed in this room", "En esta sala no se permiten solicitudes a los miembros de la sala"}.
|
||||
{"You have been banned from this room", "Has sido bloqueado en esta sala"}.
|
||||
{"Membership required to enter this room", "Necesitas ser miembro de esta sala para poder entrar"}.
|
||||
{"Password required to enter this room", "Se necesita contraseña para entrar en esta sala"}.
|
||||
{"Incorrect password", "Contraseña incorrecta"}.
|
||||
{"Administrator privileges required", "Se necesita privilegios de administrador"}.
|
||||
{"Moderator privileges required", "Se necesita privilegios de moderador"}.
|
||||
{"JID ~s is invalid", "El JID ~s no es válido"}.
|
||||
{"Nickname ~s does not exist in the room", "El apodo ~s no existe en la sala"}.
|
||||
{"Invalid affiliation: ~s", "Afiliación no válida: ~s"}.
|
||||
{"Invalid role: ~s", "Rol no válido: ~s"}.
|
||||
{"Owner privileges required", "Se requieren privilegios de propietario de la sala"}.
|
||||
{"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"}.
|
||||
|
||||
% web/ejabberd_web_admin.erl
|
||||
{"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"}.
|
||||
{"Delete Selected", "Eliminar los seleccionados"}.
|
||||
{"Submit", "Enviar"}.
|
||||
{"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"}.
|
||||
{"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 S2S de salida"}.
|
||||
{"Change Password", "Cambiar contraseña"}.
|
||||
{"Connected Resources:", "Recursos conectados:"}.
|
||||
{"Password:", "Contraseña:"}.
|
||||
{"None", "Ninguno"}.
|
||||
{"Node ", "Nodo"}.
|
||||
{"DB Management", "Gestión de la BD"}.
|
||||
{"Listened Ports Management", "Gestión de los puertos de escucha"}.
|
||||
{"Restart", "Reiniciar"}.
|
||||
{"Stop", "Detener"}.
|
||||
{"RPC call error", "Error en la llamada RPC"}.
|
||||
{"DB Tables at ", "Tablas de la BD en "}.
|
||||
{"Name", "Nombre"}.
|
||||
{"Storage Type", "Tipo de almacenamiento"}.
|
||||
{"Size", "Tamaño"}.
|
||||
{"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", "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 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"}.
|
||||
{"CPU Time", "Tiempo de procesador consumido"}.
|
||||
{"Transactions commited", "Transacciones realizadas"}.
|
||||
{"Transactions aborted", "Transacciones abortadas"}.
|
||||
{"Transactions restarted", "Transacciones reiniciadas"}.
|
||||
{"Transactions logged", "Transacciones almacenadas"}.
|
||||
{"Port", "Puerto"}.
|
||||
{"Module", "Módulo"}.
|
||||
{"Options", "Opciones"}.
|
||||
{"Update", "Actualizar"}.
|
||||
{"Delete", "Eliminar"}.
|
||||
{"Add User", ""}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin, 2004 Process One", ""}.
|
||||
{"Offline messages", ""}.
|
||||
{"Last Activity", ""}.
|
||||
{"Never", ""}.
|
||||
{"~s offline messages queue", ""}.
|
||||
{"Time", ""}.
|
||||
{"From", ""}.
|
||||
{"To", ""}.
|
||||
{"Packet", ""}.
|
||||
{"Offline messages:", ""}.
|
||||
{"Roster", ""}.
|
||||
{"Nickname", ""}.
|
||||
{"Subscription", ""}.
|
||||
{"Pending", ""}.
|
||||
{"Groups", ""}.
|
||||
{"Remove", ""}.
|
||||
{"Add JID", ""}.
|
||||
{"User ", ""}.
|
||||
{"Roster of ", ""}.
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
% End:
|
||||
+184
-23
@@ -1,24 +1,46 @@
|
||||
% $Id$
|
||||
|
||||
% jlib.hrl
|
||||
{"No resource provided", ""}.
|
||||
{"Illegal resource format", ""}.
|
||||
{"Resource conflict", ""}.
|
||||
|
||||
% mod_configure.erl
|
||||
{"DB Tables Configuration", "Configuration des tables de BD"}.
|
||||
{"Choose storage type of tables", "Choisissez un type de stockage pour les tables"}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"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"}.
|
||||
@@ -30,24 +52,33 @@
|
||||
{"Running Nodes", "Noeuds actifs"}.
|
||||
{"Stopped Nodes", "Noeuds arrêtés"}.
|
||||
{"Host Name", "Nom du serveur"}.
|
||||
{"ACLs", "ACLs"}.
|
||||
{"Access Control Lists", ""}.
|
||||
{"Access Rules", "Règles d'accès"}.
|
||||
{"Remove Users", "Supprimer des utilisateurs"}.
|
||||
{"DB", "BD"}.
|
||||
{"Modules", "Modules"}.
|
||||
{"Start Modules", "Modules de démarrage"}.
|
||||
{"Stop Modules", "Modules d'arrêt"}.
|
||||
{"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", ""}.
|
||||
|
||||
% mod_vcard.erl
|
||||
{"You need a x:data capable client to search",
|
||||
{"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"}.
|
||||
{"Users Search", "Rechercher des utilisateurs"}.
|
||||
{"Search users in ", "Rechercher des utilisateurs "}.
|
||||
{"Fill in fields to search for any matching Jabber User",
|
||||
"Remplissez les champs pour rechercher n'importe quel utilisateur Jabber correspondant"}.
|
||||
{"Users Search Results", "Résultat de la recherche des utilisateurs"}.
|
||||
{"Results of search in ", "Résultat de la recherche des utilisateurs "}.
|
||||
|
||||
{"User", "Utilisateur"}.
|
||||
{"Full Name", "Nom complet"}.
|
||||
@@ -62,9 +93,25 @@
|
||||
{"Organization Name", "Nom de l'organisation"}.
|
||||
{"Organization Unit", "Unité de l'organisation"}.
|
||||
|
||||
% 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", ""}.
|
||||
{"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: ", ""}.
|
||||
{"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?",
|
||||
@@ -82,9 +129,123 @@
|
||||
{"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", ""}.
|
||||
{"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", ""}.
|
||||
{"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-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", ""}.
|
||||
{"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", ""}.
|
||||
{"Add User", ""}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin, 2004 Process One", ""}.
|
||||
{"Offline messages", ""}.
|
||||
{"Last Activity", ""}.
|
||||
{"Never", ""}.
|
||||
{"~s offline messages queue", ""}.
|
||||
{"Time", ""}.
|
||||
{"From", ""}.
|
||||
{"To", ""}.
|
||||
{"Packet", ""}.
|
||||
{"Offline messages:", ""}.
|
||||
{"Roster", ""}.
|
||||
{"Nickname", ""}.
|
||||
{"Subscription", ""}.
|
||||
{"Pending", ""}.
|
||||
{"Groups", ""}.
|
||||
{"Remove", ""}.
|
||||
{"Add JID", ""}.
|
||||
{"User ", ""}.
|
||||
{"Roster of ", ""}.
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
% $Id$
|
||||
|
||||
% jlib.hrl
|
||||
{"No resource provided", "Geen bron opgegeven"}.
|
||||
{"Illegal resource format", "Ongeldig bronformaat"}.
|
||||
{"Resource conflict", "Bronnenconflict"}.
|
||||
|
||||
% mod_configure.erl
|
||||
{"DB Tables Configuration at ", "Databasetabellenconfiguratie op "}.
|
||||
{"Choose storage type of tables", "Opslagmethode voor tabellen kiezen"}.
|
||||
{"RAM copy", "Naar RAM kopiëren"}.
|
||||
{"RAM and disc copy", "RAM- en schijfkopie"}.
|
||||
{"Disc only copy", "Enkel schijfkopie"}.
|
||||
{"Remote copy", "Afstandskopie"}.
|
||||
{"Stop Modules at ", "Modules stoppen op "}.
|
||||
{"Choose modules to stop", "Selecteer de modules die u wilt stoppen"}.
|
||||
{"Start Modules at ", "Modules starten op "}.
|
||||
{"Enter list of {Module, [Options]}", "Voer lijst met op te starten modules in in volgende vorm: {Module, [Opties]}"}.
|
||||
{"List of modules to start", "Lijst met op te starten modules"}.
|
||||
{"Backup to File at ", "Backup naar bestand maken op "}.
|
||||
{"Enter path to backup file", "Voer pad naar backupbestand in"}.
|
||||
{"Path to File", "Pad naar bestand"}.
|
||||
{"Restore Backup from File at ", "Backup herstellen vanaf bestand op "}.
|
||||
{"Dump Backup to Text File at ", "Backup dumpen naar tekstbestand op "}.
|
||||
{"Enter path to text file", "Voer pad naar tekstbestand in"}.
|
||||
{"Import User from File at ", "Importeer gebruiker via bestand op "}.
|
||||
{"Enter path to jabberd1.4 spool file", "Voer pad van jabberd1.4-spool-bestand in"}.
|
||||
{"Import Users from Dir at ", "Gebruikers importeren vanaf directory op "}.
|
||||
{"Enter path to jabberd1.4 spool dir", "Voer pad naar jabberd1.4-spool-directory in"}.
|
||||
{"Path to Dir", "Pad naar directory"}.
|
||||
{"Hostname Configuration", "Hostnaam configuratie"}.
|
||||
{"Choose host name", "Hostnaam kiezen"}.
|
||||
{"Host name", "Hostnaam"}.
|
||||
{"Access Control List Configuration", "Access Control List Configuratie"}.
|
||||
{"Access control lists", "Access control lists"}.
|
||||
{"Access Configuration", "Toegangsconfiguratie"}.
|
||||
{"Access rules", "Access rules"}.
|
||||
{"Remove Users", "Gebruikers verwijderen"}.
|
||||
{"Choose users to remove", "Selecteer de gebruikers die u wilt verwijderen"}.
|
||||
{"Administration of ", "Beheer van "}.
|
||||
{"Action on user", "Actie op gebruiker"}.
|
||||
{"Edit Properties", "Eigenschappen bewerken"}.
|
||||
{"Remove User", "Gebruiker verwijderen"}.
|
||||
|
||||
% mod_disco.erl
|
||||
{"Configuration", "Configuratie"}.
|
||||
{"Online Users", "Online gebruikers"}.
|
||||
{"All Users", "Alle gebruikers"}.
|
||||
{"Outgoing S2S connections", "Uitgaande S2S verbindingen"}.
|
||||
{"To ~s", "Naar ~s"}.
|
||||
{"From ~s", "Van ~s"}.
|
||||
{"Running Nodes", "Draaiende nodes"}.
|
||||
{"Stopped Nodes", "Gestopte nodes"}.
|
||||
{"Host Name", "Hostnaam"}.
|
||||
{"Access Control Lists", "Access Control Lists"}.
|
||||
{"Access Rules", "Access Rules"}.
|
||||
{"Remove Users", "Gebruikers verwijderen"}.
|
||||
{"DB", "Database"}.
|
||||
{"Modules", "Modules"}.
|
||||
{"Start Modules", "Modules starten"}.
|
||||
{"Stop Modules", "Modules stoppen"}.
|
||||
{"Backup Management", "Backupbeheer"}.
|
||||
{"Import users from jabberd1.4 spool files", "Importeer jabberd1.4 gebruikers via spool-bestanden"}.
|
||||
{"Backup", "Backup"}.
|
||||
{"Restore", "Backup herstellen"}.
|
||||
{"Dump to Text File", "Naar tekstbestand dumpen"}.
|
||||
{"Import File", "Bestand importeren"}.
|
||||
{"Import Directory", "Directory importeren"}.
|
||||
|
||||
% mod_register.erl
|
||||
{"Choose a username and password to register with this server",
|
||||
"Kies een gebruikersnaam en een wachtwoord om u te registreren op deze server"}.
|
||||
|
||||
% mod_vcard.erl
|
||||
{"Erlang Jabber Server\nCopyright (c) 2002-2004 Alexey Shchepin",
|
||||
"Erlang Jabber Server\nCopyright (c) 2002-2004 Alexey Shchepin"}.
|
||||
{"ejabberd vCard module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd vCard module\nCopyright (c) 2003-2004 Alexey Shchepin"}.
|
||||
{"You need an x:data capable client to search",
|
||||
"U hebt een x:data compatibele client nodig om te kunnen zoeken"}.
|
||||
{"Search users in ", "Gebruikers zoeken in "}.
|
||||
{"Fill in fields to search for any matching Jabber User",
|
||||
"Vul de velden in om te zoeken naar Jabbergebruikers op deze server"}.
|
||||
{"Results of search in ", "Zoekresultaten van "}.
|
||||
|
||||
{"User", "Gebruiker"}.
|
||||
{"Full Name", "Volledige naam"}.
|
||||
{"Name", "Voornaam"}.
|
||||
{"Middle Name", "Tussennaam"}.
|
||||
{"Family Name", "Achternaam"}.
|
||||
{"Nickname", "Bijnaam"}.
|
||||
{"Birthday", "Geboortedatum"}.
|
||||
{"Country", "Land"}.
|
||||
{"City", "Plaats"}.
|
||||
{"email", "E-mail"}.
|
||||
{"Organization Name", "Organisatie"}.
|
||||
{"Organization Unit", "Afdeling"}.
|
||||
|
||||
% mod_pubsub/mod_pubsub.erl
|
||||
{"ejabberd pub/sub module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"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",
|
||||
"U hebt een x:data compatibele client nodig om een bijnaam te kunnen registreren"}.
|
||||
{"Nickname Registration at ", "Registratie van bijnaam op "}.
|
||||
{"Enter nickname you want to register", "Voer de bijnaam in die u wilt registreren"}.
|
||||
{"ejabberd MUC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd MUC module\nCopyright (c) 2003-2004 Alexey Shchepin"}.
|
||||
{"Only service administrators are allowed to send service messages",
|
||||
"Alleen service-beheerders hebben de toelating om service-berichten te versturen"}.
|
||||
{"Room creation is not allowed by service policy",
|
||||
"Een kamer creëren is niet toegestaan door de service policy"}.
|
||||
{"Conference room does not exist", "Conferentiekamer bestaat niet"}.
|
||||
{"Access denied by service policy", "Toegang geweigerd door service policy"}.
|
||||
{"You must fill in field \"nick\" in the form",
|
||||
"U moet het veld \"nick\" invullen in het formulier"}.
|
||||
{"Specified nickname is already registered", "De gekozen bijnaam is al geregistreerd"}.
|
||||
|
||||
% mod_muc/mod_muc_room.erl
|
||||
{" has set the subject to: ", " heeft het onderwerp ingesteld op: "}.
|
||||
{"You need an x:data-capable client to configure room",
|
||||
"U hebt een x:data compatibele client nodig om een kamer te kunnen configureren"}.
|
||||
{"Configuration for ", "Configuratie voor "}.
|
||||
{"Room title", "Kamernaam"}.
|
||||
{"Allow users to change subject?", "Gebruikers toestaan het onderwerp te wijzigen"}.
|
||||
{"Allow users to query other users?",
|
||||
"Gebruikers toestaan om andere gebruikers te query-en"}.
|
||||
{"Allow users to send private messages?",
|
||||
"Gebruikers toestaan om privéberichten te versturen"}.
|
||||
{"Make room public searchable?", "Kamer doorzoekbaar maken"}.
|
||||
{"Make participants list public?", "Deelnemerslijst publiek maken"}.
|
||||
{"Make room persistent?", "Kamer blijvend maken"}.
|
||||
{"Make room moderated?", "Kamer moderated maken"}.
|
||||
{"Default users as members?",
|
||||
"Gebruikers standaard als leden instellen"}.
|
||||
{"Make room members only?",
|
||||
"Kamer enkel toegankelijk maken voor leden"}.
|
||||
{"Allow users to send invites?",
|
||||
"Gebruikers toestaan om uitnodigingen te sturen"}.
|
||||
{"Make room password protected?", "Kamer beveiligen met een wachtwoord"}.
|
||||
{"Password", "Wachtwoord"}.
|
||||
{"Make room anonymous?", "Kamer anoniem maken"}.
|
||||
{"Enable logging?", "Logs inschakelen"}.
|
||||
{"Only moderators and participants are allowed to change subject in this room",
|
||||
"Alleen moderators en deelnemers hebben de toelating om het onderwerp in deze kamer te wijzigen"}.
|
||||
{"Only moderators are allowed to change subject in this room",
|
||||
"Alleen moderators hebben de toelating om het onderwerp in deze kamer te wijzigen"}.
|
||||
{"Visitors are not allowed to send messages to all occupants",
|
||||
"Bezoekers hebben geen toelating om berichten te sturen naar alle aanwezigen"}.
|
||||
{"Only occupants are allowed to send messages to the conference",
|
||||
"Alleen aanwezigen hebben de toeglating om berichten naar de conferentie te sturen"}.
|
||||
{"It is not allowed to send normal messages to the conference",
|
||||
"Het is niet toegelaten om normale berichten naar de kamer te sturen"}.
|
||||
{"It is not allowed to send private messages to the conference",
|
||||
"Het is niet toegelaten om privéberichten naar de kamer te sturen"}.
|
||||
{"Improper message type", "Onjuist berichttype"}.
|
||||
{"Nickname is already in use by another occupant", "Bijnaam is al in gebruik door een andere aanwezige"}.
|
||||
{"Nickname is registered by another person", "Bijnaam is al geregistreerd door iemand anders"}.
|
||||
{"It is not allowed to send private messages of type \"groupchat\"",
|
||||
"Het is niet toegelaten om privéberichten van het type \"groupchat\" te versturen"}.
|
||||
{"Recipient is not in the conference room", "Ontvanger is niet in de conferentiekamer"}.
|
||||
{"Only occupants are allowed to send queries to the conference",
|
||||
"Alleen aanwezeigen hebben de toegelating om verzoeken te verzenden naar de conferentie"}.
|
||||
{"Queries to the conference members are not allowed in this room",
|
||||
"Verzoeken aan de conferentieleden zijn niet toegelaten in deze kamer"}.
|
||||
{"You have been banned from this room", "U bent verbannen uit deze kamer"}.
|
||||
{"Membership required to enter this room", "Lidmaatschap is vereist om deze kamer te kunnen betreden"}.
|
||||
{"Password required to enter this room", "Wachtwoord is vereist om deze kamer te kunnen betreden"}.
|
||||
{"Incorrect password", "Foutief wachtwoord"}.
|
||||
{"Administrator privileges required", "Beheerdersprivileges zijn vereist"}.
|
||||
{"Moderator privileges required", "Moderatorprivileges zijn vereist"}.
|
||||
{"JID ~s is invalid", "JID ~s is ongeldig"}.
|
||||
{"Nickname ~s does not exist in the room", "Bijnaam ~s bestaat niet in de kamer"}.
|
||||
{"Invalid affiliation: ~s", "Ongeldige aansluiting: ~s"}.
|
||||
{"Invalid role: ~s", "Ongeldige rol: ~s"}.
|
||||
{"Owner privileges required", "Eigenaarsprivileges zijn vereist"}.
|
||||
{"private, ", "privé, "}.
|
||||
|
||||
% mod_irc/mod_irc.erl
|
||||
{"ejabberd IRC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd IRC module\nCopyright (c) 2003-2004 Alexey Shchepin"}.
|
||||
{"You need an x:data capable client to configure mod_irc settings",
|
||||
"U hebt een x:data-compatibele client nodig om de instellingen van mod_irc te configureren"}.
|
||||
{"Registration in mod_irc for ", "Registratie in mod_irc voor "}.
|
||||
{"Enter username and encodings you wish to use for connecting to IRC servers",
|
||||
"Voer de gebruikersnaam en coderingen in die u wilt gebruiken voor de verbindingen met IRC servers"}.
|
||||
{"IRC Username", "IRC gebruikersnaam"}.
|
||||
{"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.", "Indien u verschillende coderingen wilt specificeren voor IRC servers, dan moet u in deze lijst waaren in het formaat: '{\"irc server\", \"codering\"}'. Standaard gebruikt deze service codering \"~s\"."}.
|
||||
{"Example: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}].", "Voorbeeld: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}]."}.
|
||||
{"Encodings", "Coderingen"}.
|
||||
|
||||
% web/ejabberd_web_admin.erl
|
||||
{"ejabberd administration", "Beheer van ejabberd"}.
|
||||
{"Users", "Gebruikers"}.
|
||||
{"Nodes", "Nodes"}.
|
||||
{"Statistics", "Statistieken"}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin", "ejabberd (c) 2002-2004 Alexey Shchepin"}.
|
||||
{"(raw)", "(ruw)"}.
|
||||
{"submitted", "verzonden"}.
|
||||
{"bad format", "slecht formaat"}.
|
||||
{"raw", "ruw"}.
|
||||
{"ejabberd access control lists configuration", "ejabberd access control lists configuratie"}.
|
||||
{"Delete Selected", "Geselecteerde verwijderen"}.
|
||||
{"Submit", "Verzenden"}.
|
||||
{"ejabberd access rules configuration", "ejabberd access rules configuratie"}.
|
||||
{"~s access rule configuration", "~s access rule configuratie"}.
|
||||
{"ejabberd users", "ejabberd-gebruikers"}.
|
||||
{"ejabberd stats", "Statistieken van ejabberd"}.
|
||||
{"Node not found", "Node niet gevonden"}.
|
||||
{"Add New", "Nieuw toevoegen"}.
|
||||
{"Registered users", "Geregistreerde gebruikers"}.
|
||||
{"Authentificated users", "Aangemelde gebruikers"}.
|
||||
{"Online users", "Online gebruikers"}.
|
||||
{"Outgoing S2S servers", "Uitgaande S2S verbindingen"}.
|
||||
{"Change Password", "Wachtwoord wijzigen"}.
|
||||
{"Connected Resources:", "Verbonden bronnen:"}.
|
||||
{"Password:", "Wachtwoord:"}.
|
||||
{"None", "Geen"}.
|
||||
{"Node ", "Node"}.
|
||||
{"DB Management", "Databasebeheer"}.
|
||||
{"Listened Ports Management", "Beheer van openstaande poorten"}.
|
||||
{"Restart", "Herstarten"}.
|
||||
{"Stop", "Stoppen"}.
|
||||
{"RPC call error", "RPC oproepfout"}.
|
||||
{"DB Tables at ", "Databasetabellen op "}.
|
||||
{"Name", "Naam"}.
|
||||
{"Storage Type", "Opslagmethode"}.
|
||||
{"Size", "Grootte"}.
|
||||
{"Memory", "Geheugen"}.
|
||||
{"Backup Management at ", "Backupbeheer op "}.
|
||||
{"Store a backup in a file", "Backup opslaan in een bestand"}.
|
||||
{"OK", "OK"}.
|
||||
{"Restore a backup from a file", "Backup herstellen vanaf een bestand"}.
|
||||
{"Install a database fallback from a file", "Reservedatabase installeren vanaf een bestand"}.
|
||||
{"Dump a database in a text file", "Database dumpen in een tekstbestand"}.
|
||||
{"Restore a database from a text file", "Database herstellen vanaf een tekstbestand"}.
|
||||
{"Listened Ports at ", "Geopende poorten op "}.
|
||||
{"~p statistics", "~p statistieken"}.
|
||||
{"Uptime", "Uptime"}.
|
||||
{"CPU Time", "Processortijd"}.
|
||||
{"Transactions commited", "Transacties bevestigd"}.
|
||||
{"Transactions aborted", "Transacties afgebroken"}.
|
||||
{"Transactions restarted", "Transacties herstart"}.
|
||||
{"Transactions logged", "Transacties gelogd"}.
|
||||
{"Port", "Poort"}.
|
||||
{"Module", "Module"}.
|
||||
{"Options", "Opties"}.
|
||||
{"Update", "Bijwerken"}.
|
||||
{"Delete", "Verwijderen"}.
|
||||
{"Add User", "Gebruiker toevoegen"}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin, 2004 Process One", "ejabberd (c) 2002-2004 Alexey Shchepin, 2004 Process One"}.
|
||||
{"Offline messages", "Offline berichten"}.
|
||||
{"Last Activity", "Laatste activiteit"}.
|
||||
{"Never", "Nooit"}.
|
||||
{"~s offline messages queue", "~s offline berichten in de wachtrij"}.
|
||||
{"Time", "Tijd"}.
|
||||
{"From", "Van"}.
|
||||
{"To", "Aan"}.
|
||||
{"Packet", "Pakket"}.
|
||||
{"Offline messages:", "Offline berichten:"}.
|
||||
{"Roster", "Roster"}.
|
||||
{"Nickname", "Bijnaam"}.
|
||||
{"Subscription", "Inschrijving"}.
|
||||
{"Pending", "Bezig"}.
|
||||
{"Groups", "Groepen"}.
|
||||
{"Remove", "Verwijderen"}.
|
||||
{"Add JID", "JID toevoegen"}.
|
||||
{"User ", "Gebruiker "}.
|
||||
{"Roster of ", "Roster van "}.
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
% End:
|
||||
+209
-27
@@ -1,24 +1,46 @@
|
||||
% $Id$
|
||||
|
||||
% jlib.hrl
|
||||
{"No resource provided", "Не указан ресурс"}.
|
||||
{"Illegal resource format", "Неправильный формат ресурса"}.
|
||||
{"Resource conflict", "Конфликт ресурсов"}.
|
||||
|
||||
% mod_configure.erl
|
||||
{"DB Tables Configuration", "Конфигурация таблиц БД"}.
|
||||
{"DB Tables Configuration at ", "Конфигурация таблиц БД на "}.
|
||||
{"Choose storage type of tables", "Выберите тип хранения таблиц"}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"", ""}.
|
||||
{"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]}", "Введите список вида {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", "Введите путь к файлу из спула jabberd1.4"}.
|
||||
{"Import Users from Dir at ", "Импорт пользователей из директории на "}.
|
||||
{"Enter path to jabberd1.4 spool dir", "Введите путь к директории спула jabberd1.4"}.
|
||||
{"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", "Конфигурация"}.
|
||||
@@ -30,24 +52,36 @@
|
||||
{"Running Nodes", "Работающие узлы"}.
|
||||
{"Stopped Nodes", "Остановленные узлы"}.
|
||||
{"Host Name", "Имя хоста"}.
|
||||
{"ACLs", "ACLs"}.
|
||||
{"Access Control Lists", "Списки управления доступом"}.
|
||||
{"Access Rules", "Правила доступа"}.
|
||||
{"Remove Users", "Удаление пользователей"}.
|
||||
{"DB", "БД"}.
|
||||
{"Modules", "Модули"}.
|
||||
{"Start Modules", "Запуск модулей"}.
|
||||
{"Stop Modules", "Остановка модулей"}.
|
||||
{"Backup Management", "Управление резервным копированием"}.
|
||||
{"Import users from jabberd1.4 spool files", "Импорт пользователей из спула jabberd1.4"}.
|
||||
{"Backup", "Резервное копирование"}.
|
||||
{"Restore", "Восстановление из резервной копии"}.
|
||||
{"Dump to Text File", "Копирование в текстовый файл"}.
|
||||
{"Import File", "Импорт из файла"}.
|
||||
{"Import Directory", "Импорт из директории"}.
|
||||
|
||||
|
||||
|
||||
% mod_register.erl
|
||||
{"Choose a username and password to register with this server",
|
||||
"Выберите имя пользователя и пароль для регистрации на этом сервере"}.
|
||||
|
||||
% mod_vcard.erl
|
||||
{"You need a x:data capable client to search",
|
||||
"Чтобы воспользоваться поиском, Вам нужен x:data-совместимый клиент"}.
|
||||
{"Users Search", "Поиск пользователей"}.
|
||||
{"Fill in fields to search for any matching Jabber User",
|
||||
"Заполните поля для поиска пользователя Jabber"}.
|
||||
{"Users Search Results", "Результат поиска пользователей"}.
|
||||
{"Erlang Jabber Server\nCopyright (c) 2002-2004 Alexey Shchepin",
|
||||
"Erlang Jabber Server\nCopyright (c) 2002-2004 Алексей Щепин"}.
|
||||
{"ejabberd vCard module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd vCard модуль\nCopyright (c) 2003-2004 Алексей Щепин"}.
|
||||
{"You need an x:data capable client to search",
|
||||
"Чтобы воспользоваться поиском, требуется x:data-совместимый клиент"}.
|
||||
{"Search users in ", "Поиск пользователей в "}.
|
||||
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)",
|
||||
"Заполните форму для поиска пользователя Jabber (Если добавить * в конец поля, то происходит поиск подстроки)"}.
|
||||
{"Results of search in ", "Результаты поиска в "}.
|
||||
|
||||
{"User", "Пользователь"}.
|
||||
{"Full Name", "Полное имя"}.
|
||||
@@ -62,9 +96,32 @@
|
||||
{"Organization Name", "Название организации"}.
|
||||
{"Organization Unit", "Отдел организации"}.
|
||||
|
||||
% mod_pubsub/mod_pubsub.erl
|
||||
{"ejabberd pub/sub module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd pub/sub модуль\nCopyright (c) 2003-2004 Алексей Щепин"}.
|
||||
|
||||
% mod_muc/mod_muc.erl
|
||||
{"You need an x:data capable client to register nickname",
|
||||
"Чтобы зарегистрировать псевдоним, требуется x:data-совместимый клиент"}.
|
||||
{"Nickname Registration at ", "Регистрация псевдонима на "}.
|
||||
{"Enter nickname you want to register", "Введите псевдоним, который Вы хотели бы зарегистрировать"}.
|
||||
{"ejabberd MUC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd MUC модуль\nCopyright (c) 2003-2004 Алексей Щепин"}.
|
||||
{"Only service administrators are allowed to send service messages",
|
||||
"Только администратор службы может посылать служебные сообщения"}.
|
||||
{"Room creation is not allowed by service policy",
|
||||
"Cоздавать конференцию не разрешено политикой службы"}.
|
||||
{"Conference room does not exist", "Конференция не существует"}.
|
||||
{"Access denied by service policy", "Доступ запрещён политикой службы"}.
|
||||
{"You must fill in field \"nick\" in the form",
|
||||
"Вы должны заполнить поле \"nick\" в форме"}.
|
||||
{"Specified nickname is already registered", "Указанный псевдоним уже зарегистрирован"}.
|
||||
|
||||
% mod_muc/mod_muc_room.erl
|
||||
|
||||
{" has set the subject to: ", " установил(а) тему: "}.
|
||||
{"You need an x:data capable client to configure room",
|
||||
"Чтобы сконфигурировать комнату, требуется x:data-совместимый клиент"}.
|
||||
{"Configuration for ", "Конфигурация "}.
|
||||
{"Room title", "Название комнаты"}.
|
||||
{"Allow users to change subject?", "Разрешить пользователям изменять тему?"}.
|
||||
{"Allow users to query other users?",
|
||||
@@ -82,10 +139,135 @@
|
||||
{"Allow users to send invites?",
|
||||
"Разрешить пользователям посылать приглашения?"}.
|
||||
{"Make room password protected?", "Сделать комнату защищённой паролем?"}.
|
||||
{"Password", "Пароль"}.
|
||||
{"Make room anonymous?", "Сделать комнату анонимной?"}.
|
||||
{"Enable logging?", "Включить журналирование?"}.
|
||||
{"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\"",
|
||||
"Нельзя посылать частные сообщения типа \"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", "JID ~s недопустимый"}.
|
||||
{"Nickname ~s does not exist in the room", "Псевдоним ~s в комнате отсутствует"}.
|
||||
{"Invalid affiliation: ~s", "Недопустимый ранг: ~s"}.
|
||||
{"Invalid role: ~s", "Недопустимая роль: ~s"}.
|
||||
{"Owner privileges required", "Требуются права владельца"}.
|
||||
{"private, ", "приватная, "}.
|
||||
|
||||
% mod_irc/mod_irc.erl
|
||||
{"ejabberd IRC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd IRC модуль\nCopyright (c) 2003-2004 Алексей Щепин"}.
|
||||
{"You need an x:data capable client to configure mod_irc settings",
|
||||
"Чтобы настроить параметры mod_irc, требуется x:data-совместимый клиент"}.
|
||||
{"Registration in mod_irc for ", "Регистрация в mod_irc для "}.
|
||||
{"Enter username and encodings you wish to use for connecting to IRC servers",
|
||||
"Введите имя пользователя и кодировки, которые будут использоваться при подключении к IRC-серверам"}.
|
||||
{"IRC Username", "Имя пользователя 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.", "Чтобы указать различные кодировки для разных серверов IRC, заполните список значениями в формате '{\"irc server\", \"encoding\"}'. По умолчанию эта служба использует кодировку \"~s\"."}.
|
||||
{"Example: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}].", "Примеры: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}]."}.
|
||||
{"Encodings", "Кодировки"}.
|
||||
|
||||
% web/ejabberd_web_admin.erl
|
||||
{"ejabberd administration", "Администрирование ejabberd"}.
|
||||
{"Users", "Пользователи"}.
|
||||
{"Nodes", "Узлы"}.
|
||||
{"Statistics", "Статистика"}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin", "ejabberd (c) 2002-2004 Алексей Щепин"}.
|
||||
{"(raw)", "(необработанный формат)"}.
|
||||
{"submitted", "отправлено"}.
|
||||
{"bad format", "неправильный формат"}.
|
||||
{"raw", "необработанный формат"}.
|
||||
{"ejabberd access control lists configuration", "Конфигурация списков управления доступом ejabberd"}.
|
||||
{"Delete Selected", "Удалить выделенные"}.
|
||||
{"Submit", "Отправить"}.
|
||||
{"ejabberd access rules configuration", "Конфигурация правил доступа ejabberd"}.
|
||||
{"~s access rule configuration", "Конфигурация правила доступа ~s"}.
|
||||
{"ejabberd users", "Пользователи ejabberd"}.
|
||||
{"ejabberd stats", "Статистика ejabberd"}.
|
||||
{"Node not found", "Узел не найден"}.
|
||||
{"Add New", "Добавить"}.
|
||||
{"Registered users", "Зарегистрированные пользователи"}.
|
||||
{"Authentificated users", "Аутентифицированные пользователи"}.
|
||||
{"Online users", "Подключённые пользователи"}.
|
||||
{"Outgoing S2S servers", "Исходящие S2S-серверы"}.
|
||||
{"Change Password", "Сменить пароль"}.
|
||||
{"Connected Resources:", "Подключённые ресурсы:"}.
|
||||
{"Password:", "Пароль:"}.
|
||||
{"None", "Нет"}.
|
||||
{"Node ", "Узел "}.
|
||||
{"DB Management", "Управление БД"}.
|
||||
{"Listened Ports Management", "Управление прослушиваемыми портами"}.
|
||||
{"Restart", "Перезапустить"}.
|
||||
{"Stop", "Остановить"}.
|
||||
{"RPC call error", "Ошибка вызова RPC"}.
|
||||
{"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", "статистика узла ~p"}.
|
||||
{"Uptime", "Время работы"}.
|
||||
{"CPU Time", "Процессорное время"}.
|
||||
{"Transactions commited", "Транзакции завершенные"}.
|
||||
{"Transactions aborted", "Транзакции отмененные"}.
|
||||
{"Transactions restarted", "Транзакции перезапущенные"}.
|
||||
{"Transactions logged", "Транзакции запротоколированные"}.
|
||||
{"Port", "Порт"}.
|
||||
{"Module", "Модуль"}.
|
||||
{"Options", "Параметры"}.
|
||||
{"Update", "Обновить"}.
|
||||
{"Delete", "Удалить"}.
|
||||
{"Add User", "Добавить пользователя"}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin, 2004 Process One", "ejabberd (c) 2002-2004 Алексей Щепин, 2004 Process One"}.
|
||||
{"Offline messages", "Офлайновые сообщения"}.
|
||||
{"Last Activity", "Последнее подключение"}.
|
||||
{"Never", "Никогда"}.
|
||||
{"~s offline messages queue", "Oчередь офлайновых сообщений ~s"}.
|
||||
{"Time", "Время"}.
|
||||
{"From", "От кого"}.
|
||||
{"To", "Кому"}.
|
||||
{"Packet", "Пакет"}.
|
||||
{"Offline messages:", "Офлайновые сообщения:"}.
|
||||
{"Roster", "Ростер"}.
|
||||
{"Nickname", "Псевдоним"}.
|
||||
{"Subscription", "Подписка"}.
|
||||
{"Pending", "Ожидание"}.
|
||||
{"Groups", "Группы"}.
|
||||
{"Remove", "Удалить"}.
|
||||
{"Add JID", "Добавить JID"}.
|
||||
{"User ", "Пользователь "}.
|
||||
{"Roster of ", "Ростер пользователя "}.
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
% $Id$
|
||||
|
||||
% jlib.hrl
|
||||
{"No resource provided", "Не вказаний ресурс"}.
|
||||
{"Illegal resource format", "Неправильний формат ресурсу"}.
|
||||
{"Resource conflict", "Конфлікт ресурсів"}.
|
||||
|
||||
% mod_configure.erl
|
||||
{"DB Tables Configuration at ", "Конфігурація таблиць БД на "}.
|
||||
{"Choose storage type of tables", "Оберіть тип збереження таблиць"}.
|
||||
{"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]}", "Введіть перелік такого виду {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", "Введіть шлях до файла зі спула jabberd1.4"}.
|
||||
{"Import Users from Dir at ", "Імпортування користувача з директорії на "}.
|
||||
{"Enter path to jabberd1.4 spool dir", "Введіть шлях до директорії спула jabberd1.4"}.
|
||||
{"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", "Конфігурація"}.
|
||||
{"Online Users", "Підключені користувачі"}.
|
||||
{"All Users", "Всі користувачі"}.
|
||||
{"Outgoing S2S connections", "Вихідні S2S-з'єднання"}.
|
||||
{"To ~s", "До ~s"}.
|
||||
{"From ~s", "Від ~s"}.
|
||||
{"Running Nodes", "Працюючі вузли"}.
|
||||
{"Stopped Nodes", "Зупинені вузли"}.
|
||||
{"Host Name", "Назва хоста"}.
|
||||
{"Access Control Lists", "Списки керування доступом"}.
|
||||
{"Access Rules", "Правила доступу"}.
|
||||
{"Remove Users", "Видалення користувачів"}.
|
||||
{"DB", "БД"}.
|
||||
{"Modules", "Модулі"}.
|
||||
{"Start Modules", "Запуск модулів"}.
|
||||
{"Stop Modules", "Зупинка модулів"}.
|
||||
{"Backup Management", "Керування резервним копіюванням"}.
|
||||
{"Import users from jabberd1.4 spool files", "Імпорт користувачів зі спулу jabberd1.4"}.
|
||||
{"Backup", "Резервне копіювання"}.
|
||||
{"Restore", "Відновлення з резервної копії"}.
|
||||
{"Dump to Text File", "Копіювання в текстовий файл"}.
|
||||
{"Import File", "Імпорт з файла"}.
|
||||
{"Import Directory", "Імпорт з директорії"}.
|
||||
|
||||
% mod_register.erl
|
||||
{"Choose a username and password to register with this server",
|
||||
"Виберіть назву користувача та пароль для реєстрації на цьому сервері"}.
|
||||
|
||||
% mod_vcard.erl
|
||||
{"Erlang Jabber Server\nCopyright (c) 2002-2004 Alexey Shchepin",
|
||||
"Erlang Jabber Server\nCopyright (c) 2002-2004 Олексій Щепін"}.
|
||||
{"ejabberd vCard module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd vCard модуль\nCopyright (c) 2003-2004 Олексій Щепін"}.
|
||||
{"You need an x:data capable client to search",
|
||||
"Для пошуку необхідний x:data-придатний клієнт"}.
|
||||
{"Search users in ", "Пошук користувачів в "}.
|
||||
{"Fill in fields to search for any matching Jabber User",
|
||||
"Заповніть поля для пошуку користувача Jabber"}.
|
||||
{"Results of search in ", "Результати пошуку в "}.
|
||||
|
||||
{"User", "Користувач"}.
|
||||
{"Full Name", "Повне ім'я"}.
|
||||
{"Name", "Ім'я"}.
|
||||
{"Middle Name", "По-батькові"}.
|
||||
{"Family Name", "Прізвище"}.
|
||||
{"Nickname", "Псевдонім"}.
|
||||
{"Birthday", "День народження"}.
|
||||
{"Country", "Країна"}.
|
||||
{"City", "Місто"}.
|
||||
{"email", "email"}.
|
||||
{"Organization Name", "Назва організації"}.
|
||||
{"Organization Unit", "Відділ організації"}.
|
||||
|
||||
% mod_pubsub/mod_pubsub.erl
|
||||
{"ejabberd pub/sub module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd pub/sub модуль\nCopyright (c) 2003-2004 Олексій Щепін"}.
|
||||
|
||||
% mod_muc/mod_muc.erl
|
||||
{"You need an x:data capable client to register nickname",
|
||||
"Для реєстрації псевдоніму необхідний x:data-придатний клієнт"}.
|
||||
{"Nickname Registration at ", "Реєстрація псевдоніма на "}.
|
||||
{"Enter nickname you want to register", "Введіть псевдонім, який ви хочете зареєструвати"}.
|
||||
{"ejabberd MUC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd MUC модуль\nCopyright (c) 2003-2004 Алексей Щепин"}.
|
||||
{"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",
|
||||
"Вам необхідно заповнити поле \"nick\" у формі"}.
|
||||
{"Specified nickname is already registered", "Вказаний псевдонім вже зареєстрований"}.
|
||||
|
||||
% mod_muc/mod_muc_room.erl
|
||||
{" has set the subject to: ", " встановив(ла) тему: "}.
|
||||
{"You need an x:data capable client to configure room",
|
||||
"Для конфігурування кімнати необхідний x:data-придатний кліент"}.
|
||||
{"Configuration for ", "Конфігурація "}.
|
||||
{"Room title", "Назва кімнати"}.
|
||||
{"Allow users to change subject?", "Дозволити користувачам змінювати тему?"}.
|
||||
{"Allow users to query other users?",
|
||||
"Дозволити iq-запити до користувачів?"}.
|
||||
{"Allow users to send private messages?",
|
||||
"Дозволити приватні повідомлення?"}.
|
||||
{"Make room public searchable?", "Зробити кімнату видимою всім?"}.
|
||||
{"Make participants list public?", "Зробити список учасників видимим всім?"}.
|
||||
{"Make room persistent?", "Зробити кімнату постійною?"}.
|
||||
{"Make room moderated?", "Зробити кімнату модерованою?"}.
|
||||
{"Default users as members?",
|
||||
"Зробити користувачів учасниками за замовчуванням?"}.
|
||||
{"Make room members only?",
|
||||
"Кімната тільки для зареєтрованых учасників?"}.
|
||||
{"Allow users to send invites?",
|
||||
"Дозволити користувачам посилати запрошення?"}.
|
||||
{"Make room password protected?", "Зробити кімнату захищеною паролем?"}.
|
||||
{"Password", "Пароль"}.
|
||||
{"Make room anonymous?", "Зробити кімнату анонімною?"}.
|
||||
{"Enable logging?", "Включити журнал роботи?"}.
|
||||
{"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\"",
|
||||
"Не дозволяється посилати приватні повідомлення типу \"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", "JID ~s недопустимий"}.
|
||||
{"Nickname ~s does not exist in the room", "Псевдонім ~s в кімнаті відсутній"}.
|
||||
{"Invalid affiliation: ~s", "Недопустимий ранг: ~s"}.
|
||||
{"Invalid role: ~s", "Недопустима роль: ~s"}.
|
||||
{"Owner privileges required", "Необхідні права власника"}.
|
||||
{"private, ", "приватна, "}.
|
||||
|
||||
% mod_irc/mod_irc.erl
|
||||
{"ejabberd IRC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"ejabberd IRC модуль\nCopyright (c) 2003-2004 Олексій Щепін"}.
|
||||
{"You need an x:data capable client to configure mod_irc settings",
|
||||
"Для налагодження параметрів mod_irc необхідний x:data-придатний клієнт"}.
|
||||
{"Registration in mod_irc for ", "Реєстрація в mod_irc для "}.
|
||||
{"Enter username and encodings you wish to use for connecting to IRC servers",
|
||||
"Введіть ім'я користувача та кодування, які будуть використовуватися при підключенні до IRC-серверів"}.
|
||||
{"IRC Username", "Ім'я користувача 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.", "Щоб вказати різні кодування для різних серверів IRC, заповніть список значеннями в форматі '{\"irc server\", \"encoding\"}'. За замовчуванням ця служба використовує кодування \"~s\"."}.
|
||||
{"Example: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}].", "Приклад: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}]."}.
|
||||
{"Encodings", "Кодування"}.
|
||||
|
||||
% web/ejabberd_web_admin.erl
|
||||
{"ejabberd administration", "Адміністрування ejabberd"}.
|
||||
{"Users", "Користувачі"}.
|
||||
{"Nodes", "Вузли"}.
|
||||
{"Statistics", "Статистика"}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin", "ejabberd (c) 2002-2004 Олексій Щепін"}.
|
||||
{"(raw)", "(необроблений формат)"}.
|
||||
{"submitted", "відправлено"}.
|
||||
{"bad format", "неправильний формат"}.
|
||||
{"raw", "необроблений формат"}.
|
||||
{"ejabberd access control lists configuration", "Конфігурація списків керування доступом ejabberd"}.
|
||||
{"Delete Selected", "Видалити виділені"}.
|
||||
{"Submit", "Відправити"}.
|
||||
{"ejabberd access rules configuration", "Конфігурація правил доступу ejabberd"}.
|
||||
{"~s access rule configuration", "Конфігурація правила доступу ~s"}.
|
||||
{"ejabberd users", "Користувачі ejabberd"}.
|
||||
{"ejabberd stats", "Статистика ejabberd"}.
|
||||
{"Node not found", "Вузол не знайдено"}.
|
||||
{"Add New", "Добавити"}.
|
||||
{"Registered users", "Зареєстровані користувачі"}.
|
||||
{"Authentificated users", "Аутентифіковані користувачі"}.
|
||||
{"Online users", "Підключені користувачі"}.
|
||||
{"Outgoing S2S servers", "Вихідні S2S-сервери"}.
|
||||
{"Change Password", "Змінити пароль"}.
|
||||
{"Connected Resources:", "Підключені ресурси:"}.
|
||||
{"Password:", "Пароль:"}.
|
||||
{"None", "Немає"}.
|
||||
{"Node ", "Вузол "}.
|
||||
{"DB Management", "Управління БД"}.
|
||||
{"Listened Ports Management", "Управління відкритими портами"}.
|
||||
{"Restart", "Перезапустити"}.
|
||||
{"Stop", "Зупинити"}.
|
||||
{"RPC call error", "Помилка виклику RPC"}.
|
||||
{"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", "статистика вузла ~p"}.
|
||||
{"Uptime", "Час роботи сервера"}.
|
||||
{"CPU Time", "Процесорний час"}.
|
||||
{"Transactions commited", "Транзакції завершені"}.
|
||||
{"Transactions aborted", "Транзакції відмінені"}.
|
||||
{"Transactions restarted", "Транзакції перезапущені"}.
|
||||
{"Transactions logged", "Транзакції запротокольовані"}.
|
||||
{"Port", "Порт"}.
|
||||
{"Module", "Модуль"}.
|
||||
{"Options", "Параметри"}.
|
||||
{"Update", "Обновити"}.
|
||||
{"Delete", "Видалити"}.
|
||||
{"Add User", "Додати користувача"}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin, 2004 Process One", "ejabberd (c) 2002-2004 Олексій Щепін, 2004 Process One"}.
|
||||
{"Offline messages", "Офлайнові повідомлення"}.
|
||||
{"Last Activity", "Останнє підключення"}.
|
||||
{"Never", "Ніколи"}.
|
||||
{"~s offline messages queue", "Черга офлайнових повідомлень ~s"}.
|
||||
{"Time", "Час"}.
|
||||
{"From", "Від кого"}.
|
||||
{"To", "Кому"}.
|
||||
{"Packet", "Пакет"}.
|
||||
{"Offline messages:", "Офлайнові повідомлення:"}.
|
||||
{"Roster", "Ростер"}.
|
||||
{"Nickname", "Псевдонім"}.
|
||||
{"Subscription", "Підписка"}.
|
||||
{"Pending", "Очікування"}.
|
||||
{"Groups", "Групи"}.
|
||||
{"Remove", "Видалити"}.
|
||||
{"Add JID", "Додати JID"}.
|
||||
{"User ", "Користувач "}.
|
||||
{"Roster of ", "Ростер користувача "}.
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
% End:
|
||||
@@ -1,23 +1,15 @@
|
||||
# $Id$
|
||||
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@
|
||||
LIBS = @LIBS@ @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
|
||||
ERLSHLIBS = ../stringprep_drv.so
|
||||
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
@@ -28,21 +20,20 @@ all: $(OBJS) $(ERLSHLIBS)
|
||||
$(OUTDIR)/%.beam: %.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
|
||||
|
||||
#all: $(ERLSHLIBS)
|
||||
# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
|
||||
|
||||
$(ERLSHLIBS): ../%.so: %.c uni_data.c uni_norm.c
|
||||
gcc -Wall -O2 $(INCLUDES) $(LIBDIRS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) \
|
||||
-lerl_interface \
|
||||
-lei \
|
||||
-o $@ -fpic -shared \
|
||||
gcc -Wall -O2 $(CFLAGS) $(LDFLAGS) $(INCLUDES) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) $(LIBS) \
|
||||
-o $@ -fpic -shared
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(ERLSHLIBS)
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
|
||||
|
||||
@@ -4,37 +4,32 @@ include ..\Makefile.inc
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
|
||||
ALL : $(OUTDIR)\stringprep_drv.dll $(OUTDIR)\stringprep.beam
|
||||
SOURCE = stringprep_drv.c
|
||||
AUXIL = uni_data.c uni_norm.c
|
||||
OBJECT = stringprep_drv.o
|
||||
DLL = $(OUTDIR)\stringprep_drv.dll
|
||||
|
||||
ALL : $(DLL) $(OUTDIR)\stringprep.beam
|
||||
|
||||
CLEAN :
|
||||
-@erase $(OUTDIR)\stringprep_drv.dll
|
||||
-@erase $(DLL)
|
||||
-@erase $(OUTDIR)\stringprep_drv.exp
|
||||
-@erase $(OUTDIR)\stringprep_drv.lib
|
||||
-@erase stringprep_drv.obj
|
||||
-@erase stringprep_drv.pch
|
||||
-@erase vc60.idb
|
||||
-@erase $(OBJECT)
|
||||
-@erase $(OUTDIR)\stringprep.beam
|
||||
|
||||
$(OUTDIR)\stringprep.beam : stringprep.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) stringprep.erl
|
||||
|
||||
CPP=cl.exe
|
||||
CPP_PROJ=/nologo /ML /W3 /GX /O2 /I "$(ERLANG_DIR)\usr\include" /I "$(EI_DIR)\include" /D "WIN32" /D "NDEBUG" /D "_USRDLL" /D "_MBCS" /Fpstringprep_drv.pch /YX /FD /c
|
||||
CC=cl.exe
|
||||
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include"
|
||||
|
||||
.c.obj::
|
||||
$(CPP) @<<
|
||||
$(CPP_PROJ) $<
|
||||
<<
|
||||
LD=link.exe
|
||||
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib
|
||||
|
||||
LINK32=link.exe
|
||||
LINK32_FLAGS=kernel32.lib "$(ERLANG_DIR)\usr\lib\erl_dll.lib" "$(EI_DIR)\lib\ei.lib" "$(EI_DIR)\lib\erl_interface.lib" /nologo /subsystem:console /dll /pdb:none /machine:I386 /out:$(OUTDIR)\stringprep_drv.dll
|
||||
$(DLL) : $(OBJECT)
|
||||
$(LD) $(LD_FLAGS) -out:$(DLL) $(OBJECT)
|
||||
|
||||
LINK32_OBJS=stringprep_drv.obj
|
||||
|
||||
$(OUTDIR)\stringprep_drv.dll : $(LINK32_OBJS)
|
||||
$(LINK32) @<<
|
||||
$(LINK32_FLAGS) $(LINK32_OBJS)
|
||||
<<
|
||||
|
||||
stringprep_drv.obj : stringprep_drv.c
|
||||
$(OBJECT) : $(SOURCE) $(AUXIL)
|
||||
$(CC) $(CC_FLAGS) -c -Fo$(OBJECT) $(SOURCE)
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
ok = erl_ddll:load_driver(".", stringprep_drv),
|
||||
ok = erl_ddll:load_driver(ejabberd:get_so_path(), stringprep_drv),
|
||||
Port = open_port({spawn, stringprep_drv}, []),
|
||||
ets:new(stringprep_table, [set, public, named_table]),
|
||||
ets:insert(stringprep_table, {port, Port}),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <erl_driver.h>
|
||||
#include <ei.h>
|
||||
|
||||
@@ -324,11 +325,8 @@ ErlDrvEntry stringprep_driver_entry = {
|
||||
NULL /* F_PTR outputv, reserved */
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
DRIVER_INIT(stringprep_erl) /* must match name in driver_entry */
|
||||
{
|
||||
return &stringprep_driver_entry;
|
||||
return &stringprep_driver_entry;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ @SSL_CFLAGS@ @ERLANG_CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@ @SSL_LIBS@ @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
ERLSHLIBS = ../tls_drv.so
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
$(OUTDIR)/tls.beam
|
||||
|
||||
all: $(OBJS) $(ERLSHLIBS)
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
#all: $(ERLSHLIBS)
|
||||
# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
|
||||
|
||||
$(ERLSHLIBS): ../%.so: %.c
|
||||
$(CC) -Wall $(CFLAGS) $(LDFLAGS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) $(LIBS) \
|
||||
-o $@ -fpic -shared
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(ERLSHLIBS)
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
include ..\Makefile.inc
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
|
||||
OBJS = \
|
||||
$(OUTDIR)\tls.beam
|
||||
|
||||
SOURCE = tls_drv.c
|
||||
OBJECT = tls_drv.o
|
||||
DLL = $(OUTDIR)\tls_drv.dll
|
||||
|
||||
ALL : $(DLL) $(OBJS)
|
||||
|
||||
CLEAN :
|
||||
-@erase $(DLL)
|
||||
-@erase $(OUTDIR)\tls_drv.exp
|
||||
-@erase $(OUTDIR)\tls_drv.lib
|
||||
-@erase $(OBJECT)
|
||||
-@erase $(OBJS)
|
||||
|
||||
$(OUTDIR)\tls.beam : tls.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) tls.erl
|
||||
|
||||
CC=cl.exe
|
||||
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include" -I"$(OPENSSL_DIR)\include"
|
||||
|
||||
LD=link.exe
|
||||
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" "$(OPENSSL_DIR)\lib\VC\ssleay32.lib" "$(OPENSSL_DIR)\lib\VC\libeay32.lib" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib
|
||||
|
||||
$(DLL) : $(OBJECT)
|
||||
$(LD) $(LD_FLAGS) -out:$(DLL) $(OBJECT)
|
||||
|
||||
$(OBJECT) : $(SOURCE)
|
||||
$(CC) $(CC_FLAGS) -c -Fo$(OBJECT) $(SOURCE)
|
||||
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : tls.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Interface to openssl
|
||||
%%% Created : 24 Jul 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(tls).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start/0, start_link/0,
|
||||
tcp_to_tls/2, tls_to_tcp/1,
|
||||
send/2,
|
||||
recv/2, recv/3, recv_data/2,
|
||||
close/1,
|
||||
test/0]).
|
||||
|
||||
%% Internal exports, call-back functions.
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
code_change/3,
|
||||
terminate/2]).
|
||||
|
||||
-define(SET_CERTIFICATE_FILE, 1).
|
||||
-define(SET_ENCRYPTED_INPUT, 2).
|
||||
-define(SET_DECRYPTED_OUTPUT, 3).
|
||||
-define(GET_ENCRYPTED_OUTPUT, 4).
|
||||
-define(GET_DECRYPTED_INPUT, 5).
|
||||
|
||||
-record(tlssock, {tcpsock, tlsport}).
|
||||
|
||||
start() ->
|
||||
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
ok = erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
||||
Port = open_port({spawn, tls_drv}, [binary]),
|
||||
Res = port_control(Port, ?SET_CERTIFICATE_FILE, "./ssl.pem" ++ [0]),
|
||||
case Res of
|
||||
<<0>> ->
|
||||
%ets:new(iconv_table, [set, public, named_table]),
|
||||
%ets:insert(iconv_table, {port, Port}),
|
||||
{ok, Port};
|
||||
<<1, Error/binary>> ->
|
||||
{error, binary_to_list(Error)}
|
||||
end.
|
||||
|
||||
|
||||
%%% --------------------------------------------------------
|
||||
%%% The call-back functions.
|
||||
%%% --------------------------------------------------------
|
||||
|
||||
handle_call(_, _, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_cast(_, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({'EXIT', Pid, Reason}, Port) ->
|
||||
{noreply, Port};
|
||||
|
||||
handle_info({'EXIT', Port, Reason}, Port) ->
|
||||
{stop, {port_died, Reason}, Port};
|
||||
handle_info(_, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
code_change(OldVsn, State, Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
terminate(_Reason, Port) ->
|
||||
Port ! {self, close},
|
||||
ok.
|
||||
|
||||
|
||||
tcp_to_tls(TCPSocket, Options) ->
|
||||
case lists:keysearch(certfile, 1, Options) of
|
||||
{value, {certfile, CertFile}} ->
|
||||
ok = erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
||||
Port = open_port({spawn, tls_drv}, [binary]),
|
||||
case port_control(Port, ?SET_CERTIFICATE_FILE,
|
||||
CertFile ++ [0]) of
|
||||
<<0>> ->
|
||||
{ok, #tlssock{tcpsock = TCPSocket, tlsport = Port}};
|
||||
<<1, Error/binary>> ->
|
||||
{error, binary_to_list(Error)}
|
||||
end;
|
||||
false ->
|
||||
{error, no_certfile}
|
||||
end.
|
||||
|
||||
tls_to_tcp(#tlssock{tcpsock = TCPSocket, tlsport = Port}) ->
|
||||
port_close(Port),
|
||||
TCPSocket.
|
||||
|
||||
recv(Socket, Length) ->
|
||||
recv(Socket, Length, infinity).
|
||||
recv(#tlssock{tcpsock = TCPSocket, tlsport = Port} = TLSSock,
|
||||
Length, Timeout) ->
|
||||
case gen_tcp:recv(TCPSocket, Length, Timeout) of
|
||||
{ok, Packet} ->
|
||||
recv_data(TLSSock, Packet);
|
||||
{error, _Reason} = Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
recv_data(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
|
||||
case port_control(Port, ?SET_ENCRYPTED_INPUT, Packet) of
|
||||
<<0>> ->
|
||||
case port_control(Port, ?GET_DECRYPTED_INPUT, []) of
|
||||
<<0, In/binary>> ->
|
||||
case port_control(Port, ?GET_ENCRYPTED_OUTPUT, []) of
|
||||
<<0, Out/binary>> ->
|
||||
case gen_tcp:send(TCPSocket, Out) of
|
||||
ok ->
|
||||
{ok, In};
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
<<1, Error/binary>> ->
|
||||
{error, binary_to_list(Error)}
|
||||
end;
|
||||
<<1, Error/binary>> ->
|
||||
{error, binary_to_list(Error)}
|
||||
end;
|
||||
<<1, Error/binary>> ->
|
||||
{error, binary_to_list(Error)}
|
||||
end.
|
||||
|
||||
send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
|
||||
case port_control(Port, ?SET_DECRYPTED_OUTPUT, Packet) of
|
||||
<<0>> ->
|
||||
case port_control(Port, ?GET_ENCRYPTED_OUTPUT, []) of
|
||||
<<0, Out/binary>> ->
|
||||
gen_tcp:send(TCPSocket, Out);
|
||||
<<1, Error/binary>> ->
|
||||
{error, binary_to_list(Error)}
|
||||
end;
|
||||
<<1, Error/binary>> ->
|
||||
{error, binary_to_list(Error)}
|
||||
end.
|
||||
|
||||
|
||||
close(#tlssock{tcpsock = TCPSocket, tlsport = Port}) ->
|
||||
gen_tcp:close(TCPSocket),
|
||||
port_close(Port).
|
||||
|
||||
|
||||
test() ->
|
||||
ok = erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
||||
Port = open_port({spawn, tls_drv}, [binary]),
|
||||
io:format("open_port: ~p~n", [Port]),
|
||||
PCRes = port_control(Port, ?SET_CERTIFICATE_FILE, "./ssl.pem" ++ [0]),
|
||||
io:format("port_control: ~p~n", [PCRes]),
|
||||
{ok, ListenSocket} = gen_tcp:listen(1234, [binary,
|
||||
{packet, 0},
|
||||
{active, true},
|
||||
{reuseaddr, true},
|
||||
{nodelay, true}]),
|
||||
io:format("listen: ~p~n", [ListenSocket]),
|
||||
{ok, Socket} = gen_tcp:accept(ListenSocket),
|
||||
io:format("accept: ~p~n", [Socket]),
|
||||
loop(Port, Socket).
|
||||
|
||||
|
||||
loop(Port, Socket) ->
|
||||
receive
|
||||
{tcp, Socket, Data} ->
|
||||
%io:format("read: ~p~n", [Data]),
|
||||
Res = port_control(Port, ?SET_ENCRYPTED_INPUT, Data),
|
||||
io:format("SET_ENCRYPTED_INPUT: ~p~n", [Res]),
|
||||
|
||||
DIRes = port_control(Port, ?GET_DECRYPTED_INPUT, Data),
|
||||
io:format("GET_DECRYPTED_INPUT: ~p~n", [DIRes]),
|
||||
case DIRes of
|
||||
<<0, In/binary>> ->
|
||||
io:format("input: ~s~n", [binary_to_list(In)]);
|
||||
<<1, DIError/binary>> ->
|
||||
io:format("GET_DECRYPTED_INPUT error: ~p~n", [binary_to_list(DIError)])
|
||||
end,
|
||||
|
||||
EORes = port_control(Port, ?GET_ENCRYPTED_OUTPUT, Data),
|
||||
io:format("GET_ENCRYPTED_OUTPUT: ~p~n", [EORes]),
|
||||
case EORes of
|
||||
<<0, Out/binary>> ->
|
||||
gen_tcp:send(Socket, Out);
|
||||
<<1, EOError/binary>> ->
|
||||
io:format("GET_ENCRYPTED_OUTPUT error: ~p~n", [binary_to_list(EOError)])
|
||||
end,
|
||||
|
||||
|
||||
loop(Port, Socket);
|
||||
Msg ->
|
||||
io:format("receive: ~p~n", [Msg]),
|
||||
loop(Port, Socket)
|
||||
end.
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <erl_driver.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
|
||||
#define BUF_SIZE 1024
|
||||
|
||||
typedef struct {
|
||||
ErlDrvPort port;
|
||||
SSL_CTX *ctx;
|
||||
BIO *bio_read;
|
||||
BIO *bio_write;
|
||||
SSL *ssl;
|
||||
} tls_data;
|
||||
|
||||
|
||||
static ErlDrvData tls_drv_start(ErlDrvPort port, char *buff)
|
||||
{
|
||||
tls_data *d = (tls_data *)driver_alloc(sizeof(tls_data));
|
||||
d->port = port;
|
||||
d->ctx = NULL;
|
||||
d->bio_read = NULL;
|
||||
d->bio_write = NULL;
|
||||
d->ssl = NULL;
|
||||
|
||||
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
|
||||
|
||||
return (ErlDrvData)d;
|
||||
}
|
||||
|
||||
static void tls_drv_stop(ErlDrvData handle)
|
||||
{
|
||||
tls_data *d = (tls_data *)handle;
|
||||
|
||||
if (d->ssl != NULL)
|
||||
SSL_free(d->ssl);
|
||||
|
||||
if (d->ctx != NULL)
|
||||
SSL_CTX_free(d->ctx);
|
||||
|
||||
driver_free((char *)handle);
|
||||
}
|
||||
|
||||
|
||||
#define SET_CERTIFICATE_FILE 1
|
||||
#define SET_ENCRYPTED_INPUT 2
|
||||
#define SET_DECRYPTED_OUTPUT 3
|
||||
#define GET_ENCRYPTED_OUTPUT 4
|
||||
#define GET_DECRYPTED_INPUT 5
|
||||
|
||||
|
||||
#define die_unless(cond, errstr) \
|
||||
if (!(cond)) \
|
||||
{ \
|
||||
rlen = strlen(errstr) + 1; \
|
||||
b = driver_alloc_binary(rlen); \
|
||||
b->orig_bytes[0] = 1; \
|
||||
strncpy(b->orig_bytes + 1, errstr, rlen - 1); \
|
||||
*rbuf = (char *)b; \
|
||||
return rlen; \
|
||||
}
|
||||
|
||||
|
||||
static int tls_drv_control(ErlDrvData handle,
|
||||
unsigned int command,
|
||||
char *buf, int len,
|
||||
char **rbuf, int rlen)
|
||||
{
|
||||
tls_data *d = (tls_data *)handle;
|
||||
int res;
|
||||
int size;
|
||||
ErlDrvBinary *b;
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case SET_CERTIFICATE_FILE:
|
||||
d->ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
die_unless(d->ctx, "SSL_CTX_new failed");
|
||||
|
||||
res = SSL_CTX_use_certificate_file(d->ctx, buf, SSL_FILETYPE_PEM);
|
||||
die_unless(res > 0, "SSL_CTX_use_certificate_file failed");
|
||||
|
||||
res = SSL_CTX_use_PrivateKey_file(d->ctx, buf, SSL_FILETYPE_PEM);
|
||||
die_unless(res > 0, "SSL_CTX_use_PrivateKey_file failed");
|
||||
|
||||
res = SSL_CTX_check_private_key(d->ctx);
|
||||
die_unless(res > 0, "SSL_CTX_check_private_key failed");
|
||||
|
||||
d->ssl = SSL_new(d->ctx);
|
||||
die_unless(d->ssl, "SSL_new failed");
|
||||
|
||||
d->bio_read = BIO_new(BIO_s_mem());
|
||||
d->bio_write = BIO_new(BIO_s_mem());
|
||||
|
||||
SSL_set_bio(d->ssl, d->bio_read, d->bio_write);
|
||||
|
||||
SSL_set_accept_state(d->ssl);
|
||||
break;
|
||||
case SET_ENCRYPTED_INPUT:
|
||||
die_unless(d->ssl, "SSL not initialized");
|
||||
BIO_write(d->bio_read, buf, len);
|
||||
break;
|
||||
case SET_DECRYPTED_OUTPUT:
|
||||
die_unless(d->ssl, "SSL not initialized");
|
||||
res = SSL_write(d->ssl, buf, len);
|
||||
break;
|
||||
case GET_ENCRYPTED_OUTPUT:
|
||||
die_unless(d->ssl, "SSL not initialized");
|
||||
size = BUF_SIZE + 1;
|
||||
rlen = 1;
|
||||
b = driver_alloc_binary(size);
|
||||
b->orig_bytes[0] = 0;
|
||||
while ((res = BIO_read(d->bio_write,
|
||||
b->orig_bytes + rlen, BUF_SIZE)) > 0)
|
||||
{
|
||||
//printf("%d bytes of encrypted data read from state machine\r\n", res);
|
||||
|
||||
rlen += res;
|
||||
size += BUF_SIZE;
|
||||
b = driver_realloc_binary(b, size);
|
||||
}
|
||||
b = driver_realloc_binary(b, rlen);
|
||||
*rbuf = (char *)b;
|
||||
return rlen;
|
||||
case GET_DECRYPTED_INPUT:
|
||||
if (!SSL_is_init_finished(d->ssl))
|
||||
{
|
||||
//printf("Doing SSL_accept\r\n");
|
||||
res = SSL_accept(d->ssl);
|
||||
//if (res == 0)
|
||||
// printf("SSL_accept returned zero\r\n");
|
||||
if (res < 0)
|
||||
die_unless(SSL_get_error(d->ssl, res) == SSL_ERROR_WANT_READ,
|
||||
"SSL_accept failed");
|
||||
} else {
|
||||
size = BUF_SIZE + 1;
|
||||
rlen = 1;
|
||||
b = driver_alloc_binary(size);
|
||||
b->orig_bytes[0] = 0;
|
||||
|
||||
while ((res = SSL_read(d->ssl,
|
||||
b->orig_bytes + rlen, BUF_SIZE)) > 0)
|
||||
{
|
||||
//printf("%d bytes of decrypted data read from state machine\r\n",res);
|
||||
rlen += res;
|
||||
size += BUF_SIZE;
|
||||
b = driver_realloc_binary(b, size);
|
||||
}
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
int err = SSL_get_error(d->ssl, res);
|
||||
|
||||
if (err == SSL_ERROR_WANT_READ)
|
||||
{
|
||||
//printf("SSL_read wants more data\r\n");
|
||||
//return 0;
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
b = driver_realloc_binary(b, rlen);
|
||||
*rbuf = (char *)b;
|
||||
return rlen;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
b = driver_alloc_binary(1);
|
||||
b->orig_bytes[0] = 0;
|
||||
*rbuf = (char *)b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ErlDrvEntry tls_driver_entry = {
|
||||
NULL, /* F_PTR init, N/A */
|
||||
tls_drv_start, /* L_PTR start, called when port is opened */
|
||||
tls_drv_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 */
|
||||
"tls_drv", /* char *driver_name, the argument to open_port */
|
||||
NULL, /* F_PTR finish, called when unloaded */
|
||||
NULL, /* handle */
|
||||
tls_drv_control, /* F_PTR control, port_command callback */
|
||||
NULL, /* F_PTR timeout, reserved */
|
||||
NULL /* F_PTR outputv, reserved */
|
||||
};
|
||||
|
||||
DRIVER_INIT(tls_drv) /* must match name in driver_entry */
|
||||
{
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
SSL_load_error_strings();
|
||||
return &tls_driver_entry;
|
||||
}
|
||||
|
||||
|
||||
+89
-9
@@ -18,7 +18,19 @@
|
||||
|
||||
start() ->
|
||||
ets:new(translations, [named_table, public]),
|
||||
load_dir(?MSGS_DIR),
|
||||
Dir =
|
||||
case os:getenv("EJABBERD_MSGS_PATH") of
|
||||
false ->
|
||||
case code:priv_dir(ejabberd) of
|
||||
{error, _} ->
|
||||
?MSGS_DIR;
|
||||
Path ->
|
||||
filename:join([Path, "msgs"])
|
||||
end;
|
||||
Path ->
|
||||
Path
|
||||
end,
|
||||
load_dir(Dir),
|
||||
ok.
|
||||
|
||||
load_dir(Dir) ->
|
||||
@@ -28,16 +40,18 @@ load_dir(Dir) ->
|
||||
fun(FN) ->
|
||||
case string:len(FN) > 4 of
|
||||
true ->
|
||||
string:substr(FN,
|
||||
string:len(FN) - 3) == ".msg";
|
||||
string:substr(
|
||||
FN,
|
||||
string:len(FN) - 3) == ".msg";
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, Files),
|
||||
lists:foreach(
|
||||
fun(FN) ->
|
||||
load_file(string:substr(FN, 1, string:len(FN) - 4),
|
||||
Dir ++ "/" ++ FN)
|
||||
L = ascii_tolower(
|
||||
string:substr(FN, 1, string:len(FN) - 4)),
|
||||
load_file(L, Dir ++ "/" ++ FN)
|
||||
end, MsgFiles),
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
@@ -48,18 +62,84 @@ load_file(Lang, File) ->
|
||||
case file:consult(File) of
|
||||
{ok, Terms} ->
|
||||
lists:foreach(fun({Orig, Trans}) ->
|
||||
ets:insert(translations,
|
||||
{{Lang, Orig}, Trans})
|
||||
Trans1 = case Trans of
|
||||
"" ->
|
||||
Orig;
|
||||
_ ->
|
||||
Trans
|
||||
end,
|
||||
ets:insert(translations,
|
||||
{{Lang, Orig}, Trans1})
|
||||
end, Terms);
|
||||
{error, Reason} ->
|
||||
exit(file:format_error(Reason))
|
||||
end.
|
||||
|
||||
translate(Lang, Msg) ->
|
||||
case ets:lookup(translations, {Lang, Msg}) of
|
||||
LLang = ascii_tolower(Lang),
|
||||
case ets:lookup(translations, {LLang, Msg}) of
|
||||
[{_, Trans}] ->
|
||||
Trans;
|
||||
_ ->
|
||||
Msg
|
||||
ShortLang = case string:tokens(LLang, "-") of
|
||||
[] ->
|
||||
LLang;
|
||||
[SL | _] ->
|
||||
SL
|
||||
end,
|
||||
case ShortLang of
|
||||
"en" ->
|
||||
Msg;
|
||||
LLang ->
|
||||
translate(Msg);
|
||||
_ ->
|
||||
case ets:lookup(translations, {ShortLang, Msg}) of
|
||||
[{_, Trans}] ->
|
||||
Trans;
|
||||
_ ->
|
||||
translate(Msg)
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
translate(Msg) ->
|
||||
case ?MYLANG of
|
||||
undefined ->
|
||||
Msg;
|
||||
"en" ->
|
||||
Msg;
|
||||
Lang ->
|
||||
LLang = ascii_tolower(Lang),
|
||||
case ets:lookup(translations, {LLang, Msg}) of
|
||||
[{_, Trans}] ->
|
||||
Trans;
|
||||
_ ->
|
||||
ShortLang = case string:tokens(LLang, "-") of
|
||||
[] ->
|
||||
LLang;
|
||||
[SL | _] ->
|
||||
SL
|
||||
end,
|
||||
case ShortLang of
|
||||
"en" ->
|
||||
Msg;
|
||||
Lang ->
|
||||
Msg;
|
||||
_ ->
|
||||
case ets:lookup(translations, {ShortLang, Msg}) of
|
||||
[{_, Trans}] ->
|
||||
Trans;
|
||||
_ ->
|
||||
Msg
|
||||
end
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
ascii_tolower([C | Cs]) when C >= $A, C =< $Z ->
|
||||
[C + ($a - $A) | ascii_tolower(Cs)];
|
||||
ascii_tolower([C | Cs]) ->
|
||||
[C | ascii_tolower(Cs)];
|
||||
ascii_tolower([]) ->
|
||||
[].
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@ @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
$(OUTDIR)/ejabberd_http.beam \
|
||||
$(OUTDIR)/ejabberd_web.beam \
|
||||
$(OUTDIR)/ejabberd_web_admin.beam \
|
||||
$(OUTDIR)/ejabberd_http_poll.beam
|
||||
|
||||
all: $(OBJS)
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
include ..\Makefile.inc
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
|
||||
OBJS = \
|
||||
$(OUTDIR)\ejabberd_http.beam \
|
||||
$(OUTDIR)\ejabberd_web.beam \
|
||||
$(OUTDIR)\ejabberd_web_admin.beam \
|
||||
$(OUTDIR)\ejabberd_http_poll.beam
|
||||
|
||||
ALL : $(OBJS)
|
||||
|
||||
CLEAN :
|
||||
-@erase $(OBJS)
|
||||
|
||||
$(OUTDIR)\ejabberd_http.beam : ejabberd_http.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_http.erl
|
||||
|
||||
$(OUTDIR)\ejabberd_web.beam : ejabberd_web.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_web.erl
|
||||
|
||||
$(OUTDIR)\ejabberd_web_admin.beam : ejabberd_web_admin.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_web_admin.erl
|
||||
|
||||
$(OUTDIR)\ejabberd_http_poll.beam : ejabberd_http_poll.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_http_poll.erl
|
||||
@@ -0,0 +1,822 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_http.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 27 Feb 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_http).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([start/2,
|
||||
start_link/2,
|
||||
receive_headers/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
|
||||
-record(state, {sockmod,
|
||||
socket,
|
||||
request_method,
|
||||
request_version,
|
||||
request_path,
|
||||
request_auth,
|
||||
request_keepalive,
|
||||
request_content_length,
|
||||
request_lang = "en",
|
||||
use_http_poll = false,
|
||||
use_web_admin = false,
|
||||
end_of_request = false,
|
||||
trail = ""
|
||||
}).
|
||||
|
||||
|
||||
-define(XHTML_DOCTYPE,
|
||||
"<?xml version='1.0'?>\n"
|
||||
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
|
||||
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n").
|
||||
|
||||
-define(HTML_DOCTYPE,
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n").
|
||||
|
||||
|
||||
start(SockData, Opts) ->
|
||||
supervisor:start_child(ejabberd_http_sup, [SockData, Opts]).
|
||||
|
||||
start_link({SockMod, Socket}, Opts) ->
|
||||
TLSEnabled = lists:member(tls, Opts),
|
||||
TLSOpts = lists:filter(fun({certfile, _}) -> true;
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
{SockMod1, Socket1} =
|
||||
if
|
||||
TLSEnabled ->
|
||||
inet:setopts(Socket, [{recbuf, 8192}]),
|
||||
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
|
||||
{tls, TLSSocket};
|
||||
true ->
|
||||
{SockMod, Socket}
|
||||
end,
|
||||
case SockMod1 of
|
||||
gen_tcp ->
|
||||
inet:setopts(Socket1, [{packet, http}, {recbuf, 8192}]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
UseHTTPPoll = lists:member(http_poll, Opts),
|
||||
UseWebAdmin = lists:member(web_admin, Opts),
|
||||
?DEBUG("S: ~p~n", [{UseHTTPPoll, UseWebAdmin}]),
|
||||
?INFO_MSG("started: ~p", [{SockMod1, Socket1}]),
|
||||
{ok, proc_lib:spawn_link(ejabberd_http,
|
||||
receive_headers,
|
||||
[#state{sockmod = SockMod1,
|
||||
socket = Socket1,
|
||||
use_http_poll = UseHTTPPoll,
|
||||
use_web_admin = UseWebAdmin}])}.
|
||||
|
||||
|
||||
send_text(State, Text) ->
|
||||
(State#state.sockmod):send(State#state.socket, Text).
|
||||
|
||||
|
||||
receive_headers(State) ->
|
||||
SockMod = State#state.sockmod,
|
||||
Socket = State#state.socket,
|
||||
Data = SockMod:recv(Socket, 0, 300000),
|
||||
case State#state.sockmod of
|
||||
gen_tcp ->
|
||||
NewState = process_header(State, Data),
|
||||
case NewState#state.end_of_request of
|
||||
true ->
|
||||
ok;
|
||||
_ ->
|
||||
receive_headers(NewState)
|
||||
end;
|
||||
_ ->
|
||||
case Data of
|
||||
{ok, Binary} ->
|
||||
{Request, Trail} = parse_request(
|
||||
State,
|
||||
State#state.trail ++ binary_to_list(Binary)),
|
||||
State1 = State#state{trail = Trail},
|
||||
NewState = lists:foldl(
|
||||
fun(D, S) ->
|
||||
case S#state.end_of_request of
|
||||
true ->
|
||||
S;
|
||||
_ ->
|
||||
process_header(S, D)
|
||||
end
|
||||
end, State1, Request),
|
||||
case NewState#state.end_of_request of
|
||||
true ->
|
||||
ok;
|
||||
_ ->
|
||||
receive_headers(NewState)
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end.
|
||||
|
||||
process_header(State, Data) ->
|
||||
SockMod = State#state.sockmod,
|
||||
Socket = State#state.socket,
|
||||
case Data of
|
||||
{ok, {http_request, Method, Path, Version}} ->
|
||||
KeepAlive = case Version of
|
||||
{1, 1} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end,
|
||||
State#state{request_method = Method,
|
||||
request_version = Version,
|
||||
request_path = Path,
|
||||
request_keepalive = KeepAlive};
|
||||
{ok, {http_header, _, 'Connection', _, Conn}} ->
|
||||
KeepAlive1 = case Conn of
|
||||
"keep-alive" ->
|
||||
true;
|
||||
"close" ->
|
||||
false;
|
||||
_ ->
|
||||
State#state.request_keepalive
|
||||
end,
|
||||
State#state{request_keepalive = KeepAlive1};
|
||||
{ok, {http_header, _, 'Authorization', _, Auth}} ->
|
||||
State#state{request_auth = parse_auth(Auth)};
|
||||
{ok, {http_header, _, 'Content-Length', _, SLen}} ->
|
||||
case catch list_to_integer(SLen) of
|
||||
Len when is_integer(Len) ->
|
||||
State#state{request_content_length = Len};
|
||||
_ ->
|
||||
State
|
||||
end;
|
||||
{ok, {http_header, _, 'Accept-Language', _, Langs}} ->
|
||||
State#state{request_lang = parse_lang(Langs)};
|
||||
{ok, {http_header, _, _, _, _}} ->
|
||||
State;
|
||||
{ok, http_eoh} ->
|
||||
?INFO_MSG("(~w) http query: ~w ~s~n",
|
||||
[State#state.socket,
|
||||
State#state.request_method,
|
||||
element(2, State#state.request_path)]),
|
||||
Out = process_request(State),
|
||||
send_text(State, Out),
|
||||
case State#state.request_keepalive of
|
||||
true ->
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
inet:setopts(Socket, [{packet, http}]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
#state{sockmod = SockMod,
|
||||
socket = Socket,
|
||||
use_http_poll = State#state.use_http_poll,
|
||||
use_web_admin = State#state.use_web_admin};
|
||||
_ ->
|
||||
#state{end_of_request = true}
|
||||
end;
|
||||
{error, _Reason} ->
|
||||
#state{end_of_request = true};
|
||||
_ ->
|
||||
#state{end_of_request = true}
|
||||
end.
|
||||
|
||||
process_request(#state{request_method = 'GET',
|
||||
request_path = {abs_path, Path},
|
||||
request_auth = Auth,
|
||||
request_lang = Lang,
|
||||
use_http_poll = UseHTTPPoll,
|
||||
use_web_admin = UseWebAdmin}) ->
|
||||
User = case Auth of
|
||||
{U, P} ->
|
||||
case ejabberd_auth:check_password(U, P) of
|
||||
true ->
|
||||
U;
|
||||
false ->
|
||||
unauthorized
|
||||
end;
|
||||
_ ->
|
||||
undefined
|
||||
end,
|
||||
case User of
|
||||
unauthorized ->
|
||||
make_xhtml_output(
|
||||
401,
|
||||
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||
[{xmlcdata, "401 Unauthorized"}]}]));
|
||||
_ ->
|
||||
case (catch url_decode_q_split(Path)) of
|
||||
{'EXIT', _} ->
|
||||
process_request(false);
|
||||
{NPath, Query} ->
|
||||
LQuery = case (catch parse_urlencoded(Query)) of
|
||||
{'EXIT', _Reason} ->
|
||||
[];
|
||||
LQ ->
|
||||
LQ
|
||||
end,
|
||||
LPath = string:tokens(NPath, "/"),
|
||||
Request = #request{method = 'GET',
|
||||
path = LPath,
|
||||
q = LQuery,
|
||||
user = User,
|
||||
lang = Lang},
|
||||
case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
|
||||
Request) of
|
||||
El when element(1, El) == xmlelement ->
|
||||
make_xhtml_output(200, [], El);
|
||||
{Status, Headers, El} when
|
||||
element(1, El) == xmlelement ->
|
||||
make_xhtml_output(Status, Headers, El);
|
||||
Text when is_list(Text) ->
|
||||
make_text_output(200, [], Text);
|
||||
{Status, Headers, Text} when
|
||||
is_list(Text) ->
|
||||
make_text_output(Status, Headers, Text)
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
||||
process_request(#state{request_method = 'POST',
|
||||
request_path = {abs_path, Path},
|
||||
request_auth = Auth,
|
||||
request_content_length = Len,
|
||||
request_lang = Lang,
|
||||
sockmod = SockMod,
|
||||
socket = Socket,
|
||||
use_http_poll = UseHTTPPoll,
|
||||
use_web_admin = UseWebAdmin} = State)
|
||||
when is_integer(Len) ->
|
||||
User = case Auth of
|
||||
{U, P} ->
|
||||
case ejabberd_auth:check_password(U, P) of
|
||||
true ->
|
||||
U;
|
||||
false ->
|
||||
unauthorized
|
||||
end;
|
||||
_ ->
|
||||
undefined
|
||||
end,
|
||||
case User of
|
||||
unauthorized ->
|
||||
make_xhtml_output(
|
||||
401,
|
||||
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||
[{xmlcdata, "401 Unauthorized"}]}]));
|
||||
_ ->
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
inet:setopts(Socket, [{packet, 0}]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
Data = recv_data(State, Len),
|
||||
?DEBUG("client data: ~p~n", [Data]),
|
||||
case (catch url_decode_q_split(Path)) of
|
||||
{'EXIT', _} ->
|
||||
process_request(false);
|
||||
{NPath, Query} ->
|
||||
LPath = string:tokens(NPath, "/"),
|
||||
LQuery = case (catch parse_urlencoded(Data)) of
|
||||
{'EXIT', _Reason} ->
|
||||
[];
|
||||
LQ ->
|
||||
LQ
|
||||
end,
|
||||
Request = #request{method = 'POST',
|
||||
path = LPath,
|
||||
q = LQuery,
|
||||
user = User,
|
||||
data = Data,
|
||||
lang = Lang},
|
||||
case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
|
||||
Request) of
|
||||
El when element(1, El) == xmlelement ->
|
||||
make_xhtml_output(200, [], El);
|
||||
{Status, Headers, El} when
|
||||
element(1, El) == xmlelement ->
|
||||
make_xhtml_output(Status, Headers, El);
|
||||
Text when is_list(Text) ->
|
||||
make_text_output(200, [], Text);
|
||||
{Status, Headers, Text} when is_list(Text) ->
|
||||
make_text_output(Status, Headers, Text)
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
||||
process_request(_) ->
|
||||
make_xhtml_output(
|
||||
400,
|
||||
[],
|
||||
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||
[{xmlcdata, "400 Bad Request"}]}])).
|
||||
|
||||
|
||||
recv_data(State, Len) ->
|
||||
recv_data(State, Len, []).
|
||||
|
||||
recv_data(State, 0, Acc) ->
|
||||
binary_to_list(list_to_binary(Acc));
|
||||
recv_data(State, Len, Acc) ->
|
||||
case State#state.trail of
|
||||
[] ->
|
||||
case (State#state.sockmod):recv(State#state.socket, Len, 300000) of
|
||||
{ok, Data} ->
|
||||
recv_data(State, Len - size(Data), [Acc | Data]);
|
||||
_ ->
|
||||
""
|
||||
end;
|
||||
_ ->
|
||||
Trail = State#state.trail,
|
||||
recv_data(State#state{trail = ""}, Len - length(Trail), [Acc | Trail])
|
||||
end.
|
||||
|
||||
|
||||
make_xhtml_output(Status, Headers, XHTML) ->
|
||||
Data = case lists:member(html, Headers) of
|
||||
true ->
|
||||
list_to_binary([?HTML_DOCTYPE,
|
||||
xml:element_to_string(XHTML)]);
|
||||
_ ->
|
||||
list_to_binary([?XHTML_DOCTYPE,
|
||||
xml:element_to_string(XHTML)])
|
||||
end,
|
||||
Headers1 = case lists:keysearch("Content-Type", 1, Headers) of
|
||||
{value, _} ->
|
||||
[{"Content-Length", integer_to_list(size(Data))} |
|
||||
Headers];
|
||||
_ ->
|
||||
[{"Content-Type", "text/html; charset=utf-8"},
|
||||
{"Content-Length", integer_to_list(size(Data))} |
|
||||
Headers]
|
||||
end,
|
||||
H = lists:map(fun({Attr, Val}) ->
|
||||
[Attr, ": ", Val, "\r\n"];
|
||||
(_) ->
|
||||
[]
|
||||
end, Headers1),
|
||||
SL = ["HTTP/1.1 ", integer_to_list(Status), " ",
|
||||
code_to_phrase(Status), "\r\n"],
|
||||
[SL, H, "\r\n", Data].
|
||||
|
||||
make_text_output(Status, Headers, Text) ->
|
||||
Data = list_to_binary(Text),
|
||||
Headers1 = case lists:keysearch("Content-Type", 1, Headers) of
|
||||
{value, _} ->
|
||||
[{"Content-Length", integer_to_list(size(Data))} |
|
||||
Headers];
|
||||
_ ->
|
||||
[{"Content-Type", "text/html; charset=utf-8"},
|
||||
{"Content-Length", integer_to_list(size(Data))} |
|
||||
Headers]
|
||||
end,
|
||||
H = lists:map(fun({Attr, Val}) ->
|
||||
[Attr, ": ", Val, "\r\n"]
|
||||
end, Headers1),
|
||||
SL = ["HTTP/1.1 ", integer_to_list(Status), " ",
|
||||
code_to_phrase(Status), "\r\n"],
|
||||
[SL, H, "\r\n", Data].
|
||||
|
||||
|
||||
parse_lang(Langs) ->
|
||||
case string:tokens(Langs, ",; ") of
|
||||
[First | _] ->
|
||||
First;
|
||||
[] ->
|
||||
"en"
|
||||
end.
|
||||
|
||||
|
||||
|
||||
% Code below is taken (with some modifications) from the yaws webserver, which
|
||||
% is distributed under the folowing license:
|
||||
%
|
||||
% This software (the yaws webserver) is free software.
|
||||
% Parts of this software is Copyright (c) Claes Wikstrom <klacke@hyber.org>
|
||||
% Any use or misuse of the source code is hereby freely allowed.
|
||||
%
|
||||
% 1. Redistributions of source code must retain the above copyright
|
||||
% notice as well as this list of conditions.
|
||||
%
|
||||
% 2. Redistributions in binary form must reproduce the above copyright
|
||||
% notice as well as this list of conditions.
|
||||
|
||||
|
||||
%% url decode the path and return {Path, QueryPart}
|
||||
|
||||
url_decode_q_split(Path) ->
|
||||
url_decode_q_split(Path, []).
|
||||
|
||||
url_decode_q_split([$%, $C, $2, $%, Hi, Lo | Tail], Ack) ->
|
||||
Hex = hex_to_integer([Hi, Lo]),
|
||||
url_decode_q_split(Tail, [Hex|Ack]);
|
||||
url_decode_q_split([$%, $C, $3, $%, Hi, Lo | Tail], Ack) when Hi > $9 ->
|
||||
Hex = hex_to_integer([Hi+4, Lo]),
|
||||
url_decode_q_split(Tail, [Hex|Ack]);
|
||||
url_decode_q_split([$%, $C, $3, $%, Hi, Lo | Tail], Ack) when Hi < $A ->
|
||||
Hex = hex_to_integer([Hi+4+7, Lo]),
|
||||
url_decode_q_split(Tail, [Hex|Ack]);
|
||||
url_decode_q_split([$%, Hi, Lo | Tail], Ack) ->
|
||||
Hex = hex_to_integer([Hi, Lo]),
|
||||
url_decode_q_split(Tail, [Hex|Ack]);
|
||||
url_decode_q_split([$?|T], Ack) ->
|
||||
%% Don't decode the query string here, that is parsed separately.
|
||||
{path_norm_reverse(Ack), T};
|
||||
url_decode_q_split([H|T], Ack) ->
|
||||
url_decode_q_split(T, [H|Ack]);
|
||||
url_decode_q_split([], Ack) ->
|
||||
{path_norm_reverse(Ack), []}.
|
||||
|
||||
path_norm_reverse("/" ++ T) -> start_dir(0, "/", T);
|
||||
path_norm_reverse( T) -> start_dir(0, "", T).
|
||||
|
||||
start_dir(N, Path, ".." ) -> rest_dir(N, Path, "");
|
||||
start_dir(N, Path, "/" ++ T ) -> start_dir(N , Path, T);
|
||||
start_dir(N, Path, "./" ++ T ) -> start_dir(N , Path, T);
|
||||
start_dir(N, Path, "../" ++ T ) -> start_dir(N + 1, Path, T);
|
||||
start_dir(N, Path, T ) -> rest_dir (N , Path, T).
|
||||
|
||||
rest_dir (_N, Path, [] ) -> case Path of
|
||||
[] -> "/";
|
||||
_ -> Path
|
||||
end;
|
||||
rest_dir (0, Path, [ $/ | T ] ) -> start_dir(0 , [ $/ | Path ], T);
|
||||
rest_dir (N, Path, [ $/ | T ] ) -> start_dir(N - 1, Path , T);
|
||||
rest_dir (0, Path, [ H | T ] ) -> rest_dir (0 , [ H | Path ], T);
|
||||
rest_dir (N, Path, [ _H | T ] ) -> rest_dir (N , Path , T).
|
||||
|
||||
|
||||
%% hex_to_integer
|
||||
|
||||
|
||||
hex_to_integer(Hex) ->
|
||||
case catch erlang:list_to_integer(Hex, 16) of
|
||||
{'EXIT', _} ->
|
||||
old_hex_to_integer(Hex);
|
||||
X ->
|
||||
X
|
||||
end.
|
||||
|
||||
|
||||
old_hex_to_integer(Hex) ->
|
||||
DEHEX = fun (H) when H >= $a, H =< $f -> H - $a + 10;
|
||||
(H) when H >= $A, H =< $F -> H - $A + 10;
|
||||
(H) when H >= $0, H =< $9 -> H - $0
|
||||
end,
|
||||
lists:foldl(fun(E, Acc) -> Acc*16+DEHEX(E) end, 0, Hex).
|
||||
|
||||
code_to_phrase(100) -> "Continue";
|
||||
code_to_phrase(101) -> "Switching Protocols ";
|
||||
code_to_phrase(200) -> "OK";
|
||||
code_to_phrase(201) -> "Created";
|
||||
code_to_phrase(202) -> "Accepted";
|
||||
code_to_phrase(203) -> "Non-Authoritative Information";
|
||||
code_to_phrase(204) -> "No Content";
|
||||
code_to_phrase(205) -> "Reset Content";
|
||||
code_to_phrase(206) -> "Partial Content";
|
||||
code_to_phrase(300) -> "Multiple Choices";
|
||||
code_to_phrase(301) -> "Moved Permanently";
|
||||
code_to_phrase(302) -> "Found";
|
||||
code_to_phrase(303) -> "See Other";
|
||||
code_to_phrase(304) -> "Not Modified";
|
||||
code_to_phrase(305) -> "Use Proxy";
|
||||
code_to_phrase(306) -> "(Unused)";
|
||||
code_to_phrase(307) -> "Temporary Redirect";
|
||||
code_to_phrase(400) -> "Bad Request";
|
||||
code_to_phrase(401) -> "Unauthorized";
|
||||
code_to_phrase(402) -> "Payment Required";
|
||||
code_to_phrase(403) -> "Forbidden";
|
||||
code_to_phrase(404) -> "Not Found";
|
||||
code_to_phrase(405) -> "Method Not Allowed";
|
||||
code_to_phrase(406) -> "Not Acceptable";
|
||||
code_to_phrase(407) -> "Proxy Authentication Required";
|
||||
code_to_phrase(408) -> "Request Timeout";
|
||||
code_to_phrase(409) -> "Conflict";
|
||||
code_to_phrase(410) -> "Gone";
|
||||
code_to_phrase(411) -> "Length Required";
|
||||
code_to_phrase(412) -> "Precondition Failed";
|
||||
code_to_phrase(413) -> "Request Entity Too Large";
|
||||
code_to_phrase(414) -> "Request-URI Too Long";
|
||||
code_to_phrase(415) -> "Unsupported Media Type";
|
||||
code_to_phrase(416) -> "Requested Range Not Satisfiable";
|
||||
code_to_phrase(417) -> "Expectation Failed";
|
||||
code_to_phrase(500) -> "Internal Server Error";
|
||||
code_to_phrase(501) -> "Not Implemented";
|
||||
code_to_phrase(502) -> "Bad Gateway";
|
||||
code_to_phrase(503) -> "Service Unavailable";
|
||||
code_to_phrase(504) -> "Gateway Timeout";
|
||||
code_to_phrase(505) -> "HTTP Version Not Supported".
|
||||
|
||||
|
||||
parse_auth(Orig = "Basic " ++ Auth64) ->
|
||||
case decode_base64(Auth64) of
|
||||
{error, _Err} ->
|
||||
undefined;
|
||||
Auth ->
|
||||
case string:tokens(Auth, ":") of
|
||||
[User, Pass] ->
|
||||
{User, Pass};
|
||||
_ ->
|
||||
undefined
|
||||
end
|
||||
end;
|
||||
parse_auth(_) ->
|
||||
undefined.
|
||||
|
||||
|
||||
|
||||
decode_base64([]) ->
|
||||
[];
|
||||
decode_base64([Sextet1,Sextet2,$=,$=|Rest]) ->
|
||||
Bits2x6=
|
||||
(d(Sextet1) bsl 18) bor
|
||||
(d(Sextet2) bsl 12),
|
||||
Octet1=Bits2x6 bsr 16,
|
||||
[Octet1|decode_base64(Rest)];
|
||||
decode_base64([Sextet1,Sextet2,Sextet3,$=|Rest]) ->
|
||||
Bits3x6=
|
||||
(d(Sextet1) bsl 18) bor
|
||||
(d(Sextet2) bsl 12) bor
|
||||
(d(Sextet3) bsl 6),
|
||||
Octet1=Bits3x6 bsr 16,
|
||||
Octet2=(Bits3x6 bsr 8) band 16#ff,
|
||||
[Octet1,Octet2|decode_base64(Rest)];
|
||||
decode_base64([Sextet1,Sextet2,Sextet3,Sextet4|Rest]) ->
|
||||
Bits4x6=
|
||||
(d(Sextet1) bsl 18) bor
|
||||
(d(Sextet2) bsl 12) bor
|
||||
(d(Sextet3) bsl 6) bor
|
||||
d(Sextet4),
|
||||
Octet1=Bits4x6 bsr 16,
|
||||
Octet2=(Bits4x6 bsr 8) band 16#ff,
|
||||
Octet3=Bits4x6 band 16#ff,
|
||||
[Octet1,Octet2,Octet3|decode_base64(Rest)];
|
||||
decode_base64(_CatchAll) ->
|
||||
{error, bad_base64}.
|
||||
|
||||
d(X) when X >= $A, X =<$Z ->
|
||||
X-65;
|
||||
d(X) when X >= $a, X =<$z ->
|
||||
X-71;
|
||||
d(X) when X >= $0, X =<$9 ->
|
||||
X+4;
|
||||
d($+) -> 62;
|
||||
d($/) -> 63;
|
||||
d(_) -> 63.
|
||||
|
||||
|
||||
parse_urlencoded(S) ->
|
||||
parse_urlencoded(S, nokey, [], key).
|
||||
|
||||
parse_urlencoded([$%, Hi, Lo | Tail], Last, Cur, State) ->
|
||||
Hex = hex_to_integer([Hi, Lo]),
|
||||
parse_urlencoded(Tail, Last, [Hex | Cur], State);
|
||||
|
||||
parse_urlencoded([$& | Tail], _Last, Cur, key) ->
|
||||
[{lists:reverse(Cur), ""} |
|
||||
parse_urlencoded(Tail, nokey, [], key)]; %% cont keymode
|
||||
|
||||
parse_urlencoded([$& | Tail], Last, Cur, value) ->
|
||||
V = {Last, lists:reverse(Cur)},
|
||||
[V | parse_urlencoded(Tail, nokey, [], key)];
|
||||
|
||||
parse_urlencoded([$+ | Tail], Last, Cur, State) ->
|
||||
parse_urlencoded(Tail, Last, [$\s | Cur], State);
|
||||
|
||||
parse_urlencoded([$= | Tail], _Last, Cur, key) ->
|
||||
parse_urlencoded(Tail, lists:reverse(Cur), [], value); %% change mode
|
||||
|
||||
parse_urlencoded([H | Tail], Last, Cur, State) ->
|
||||
parse_urlencoded(Tail, Last, [H|Cur], State);
|
||||
|
||||
parse_urlencoded([], Last, Cur, _State) ->
|
||||
[{Last, lists:reverse(Cur)}];
|
||||
|
||||
parse_urlencoded(undefined, _, _, _) ->
|
||||
[].
|
||||
|
||||
|
||||
% The following code is mostly taken from yaws_ssl.erl
|
||||
|
||||
parse_request(State, Data) ->
|
||||
case Data of
|
||||
[] ->
|
||||
{[], []};
|
||||
_ ->
|
||||
?DEBUG("GOT ssl data ~p~n", [Data]),
|
||||
{R, Trail} = case State#state.request_method of
|
||||
undefined ->
|
||||
{R1, Trail1} = get_req(Data),
|
||||
?DEBUG("Parsed request ~p~n", [R1]),
|
||||
{[R1], Trail1};
|
||||
_ ->
|
||||
{[], Data}
|
||||
end,
|
||||
{H, Trail2} = get_headers(Trail),
|
||||
{R ++ H, Trail2}
|
||||
end.
|
||||
|
||||
get_req("\r\n\r\n" ++ _) ->
|
||||
bad_request;
|
||||
get_req("\r\n" ++ Data) ->
|
||||
get_req(Data);
|
||||
get_req(Data) ->
|
||||
{FirstLine, Trail} = lists:splitwith(fun not_eol/1, Data),
|
||||
R = parse_req(FirstLine),
|
||||
{R, Trail}.
|
||||
|
||||
|
||||
not_eol($\r)->
|
||||
false;
|
||||
not_eol($\n) ->
|
||||
false;
|
||||
not_eol(_) ->
|
||||
true.
|
||||
|
||||
|
||||
get_word(Line)->
|
||||
{Word, T} = lists:splitwith(fun(X)-> X /= $\ end, Line),
|
||||
{Word, lists:dropwhile(fun(X) -> X == $\ end, T)}.
|
||||
|
||||
|
||||
parse_req(Line) ->
|
||||
{MethodStr, L1} = get_word(Line),
|
||||
?DEBUG("Method: ~p~n", [MethodStr]),
|
||||
case L1 of
|
||||
[] ->
|
||||
bad_request;
|
||||
_ ->
|
||||
{URI, L2} = get_word(L1),
|
||||
{VersionStr, L3} = get_word(L2),
|
||||
?DEBUG("URI: ~p~nVersion: ~p~nL3: ~p~n",
|
||||
[URI, VersionStr, L3]),
|
||||
case L3 of
|
||||
[] ->
|
||||
Method = case MethodStr of
|
||||
"GET" -> 'GET';
|
||||
"POST" -> 'POST';
|
||||
"HEAD" -> 'HEAD';
|
||||
"OPTIONS" -> 'OPTIONS';
|
||||
"TRACE" -> 'TRACE';
|
||||
"PUT" -> 'PUT';
|
||||
"DELETE" -> 'DELETE';
|
||||
S -> S
|
||||
end,
|
||||
Path = case URI of
|
||||
"*" ->
|
||||
% Is this correct?
|
||||
"*";
|
||||
P ->
|
||||
% FIXME: Handle
|
||||
% absolute URIs
|
||||
{abs_path, P}
|
||||
end,
|
||||
case VersionStr of
|
||||
[] ->
|
||||
{ok, {http_request, Method, Path, {0,9}}};
|
||||
"HTTP/1.0" ->
|
||||
{ok, {http_request, Method, Path, {1,0}}};
|
||||
"HTTP/1.1" ->
|
||||
{ok, {http_request, Method, Path, {1,1}}};
|
||||
_ ->
|
||||
bad_request
|
||||
end;
|
||||
_ ->
|
||||
bad_request
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
get_headers(Tail) ->
|
||||
get_headers([], Tail).
|
||||
|
||||
get_headers(H, Tail) ->
|
||||
case get_line(Tail) of
|
||||
{incomplete, Tail2} ->
|
||||
{H, Tail2};
|
||||
{line, Line, Tail2} ->
|
||||
get_headers(H ++ parse_line(Line), Tail2);
|
||||
{lastline, Line, Tail2} ->
|
||||
{H ++ parse_line(Line) ++ [{ok, http_eoh}], Tail2}
|
||||
end.
|
||||
|
||||
|
||||
parse_line("Connection:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'Connection', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Host:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'Host', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Accept:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'Accept', undefined, strip_spaces(Con)}}];
|
||||
parse_line("If-Modified-Since:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'If-Modified-Since', undefined, strip_spaces(Con)}}];
|
||||
parse_line("If-Match:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'If-Match', undefined, strip_spaces(Con)}}];
|
||||
parse_line("If-None-Match:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'If-None-Match', undefined, strip_spaces(Con)}}];
|
||||
parse_line("If-Range:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'If-Range', undefined, strip_spaces(Con)}}];
|
||||
parse_line("If-Unmodified-Since:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'If-Unmodified-Since', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Range:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'Range', undefined, strip_spaces(Con)}}];
|
||||
parse_line("User-Agent:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'User-Agent', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Accept-Ranges:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'Accept-Ranges', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Authorization:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'Authorization', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Keep-Alive:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'Keep-Alive', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Referer:" ++ Con) ->
|
||||
[{ok, {http_header, undefined, 'Referer', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Content-type:"++Con) ->
|
||||
[{ok, {http_header, undefined, 'Content-Type', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Content-Type:"++Con) ->
|
||||
[{ok, {http_header, undefined, 'Content-Type', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Content-Length:"++Con) ->
|
||||
[{ok, {http_header, undefined, 'Content-Length', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Content-length:"++Con) ->
|
||||
[{ok, {http_header, undefined, 'Content-Length', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Cookie:"++Con) ->
|
||||
[{ok, {http_header, undefined, 'Cookie', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Accept-Language:"++Con) ->
|
||||
[{ok, {http_header, undefined, 'Accept-Language', undefined, strip_spaces(Con)}}];
|
||||
parse_line("Accept-Encoding:"++Con) ->
|
||||
[{ok, {http_header, undefined, 'Accept-Encoding', undefined, strip_spaces(Con)}}];
|
||||
parse_line(S) ->
|
||||
case lists:splitwith(fun(C)->C /= $: end, S) of
|
||||
{Name, [$:|Val]} ->
|
||||
[{ok, {http_header, undefined, Name, undefined, strip_spaces(Val)}}];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
|
||||
is_space($\s) ->
|
||||
true;
|
||||
is_space($\r) ->
|
||||
true;
|
||||
is_space($\n) ->
|
||||
true;
|
||||
is_space($\r) ->
|
||||
true;
|
||||
is_space(_) ->
|
||||
false.
|
||||
|
||||
|
||||
strip_spaces(String) ->
|
||||
strip_spaces(String, both).
|
||||
|
||||
strip_spaces(String, left) ->
|
||||
drop_spaces(String);
|
||||
strip_spaces(String, right) ->
|
||||
lists:reverse(drop_spaces(lists:reverse(String)));
|
||||
strip_spaces(String, both) ->
|
||||
strip_spaces(drop_spaces(String), right).
|
||||
|
||||
drop_spaces([]) ->
|
||||
[];
|
||||
drop_spaces(YS=[X|XS]) ->
|
||||
case is_space(X) of
|
||||
true ->
|
||||
drop_spaces(XS);
|
||||
false ->
|
||||
YS
|
||||
end.
|
||||
|
||||
is_nb_space(X) ->
|
||||
lists:member(X, [$\s, $\t]).
|
||||
|
||||
|
||||
% ret: {line, Line, Trail} | {lastline, Line, Trail}
|
||||
|
||||
get_line(L) ->
|
||||
get_line(L, []).
|
||||
get_line("\r\n\r\n" ++ Tail, Cur) ->
|
||||
{lastline, lists:reverse(Cur), Tail};
|
||||
get_line("\r\n" ++ Tail, Cur) ->
|
||||
case Tail of
|
||||
[] ->
|
||||
{incomplete, lists:reverse(Cur) ++ "\r\n"};
|
||||
_ ->
|
||||
case is_nb_space(hd(Tail)) of
|
||||
true -> %% multiline ... continue
|
||||
get_line(Tail, [$\n, $\r | Cur]);
|
||||
false ->
|
||||
{line, lists:reverse(Cur), Tail}
|
||||
end
|
||||
end;
|
||||
get_line([H|T], Cur) ->
|
||||
get_line(T, [H|Cur]).
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_http.hrl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 4 Mar 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(request, {method,
|
||||
path,
|
||||
q = [],
|
||||
user,
|
||||
lang = "",
|
||||
data = ""
|
||||
}).
|
||||
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_http_poll.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : HTTP Polling support (JEP-0025)
|
||||
%%% Created : 4 Mar 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_http_poll).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% External exports
|
||||
-export([start_link/2,
|
||||
init/1,
|
||||
handle_event/3,
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3,
|
||||
send/2,
|
||||
recv/3,
|
||||
close/1,
|
||||
process_request/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
|
||||
-record(http_poll, {id, pid}).
|
||||
|
||||
-record(state, {id,
|
||||
key,
|
||||
output = "",
|
||||
input = "",
|
||||
waiting_input = false,
|
||||
timer}).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(DBGFSM).
|
||||
-define(FSMOPTS, [{debug, [trace]}]).
|
||||
-else.
|
||||
-define(FSMOPTS, []).
|
||||
-endif.
|
||||
|
||||
-define(HTTP_POLL_TIMEOUT, 300000).
|
||||
-define(CT, {"Content-Type", "text/xml; charset=utf-8"}).
|
||||
-define(BAD_REQUEST, [?CT, {"Set-Cookie", "ID=-3:0; expires=-1"}]).
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(ID, Key) ->
|
||||
mnesia:create_table(http_poll,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, http_poll)}]),
|
||||
supervisor:start_child(ejabberd_http_poll_sup, [ID, Key]).
|
||||
|
||||
start_link(ID, Key) ->
|
||||
gen_fsm:start_link(?MODULE, [ID, Key], ?FSMOPTS).
|
||||
|
||||
send({http_poll, FsmRef}, Packet) ->
|
||||
gen_fsm:sync_send_all_state_event(FsmRef, {send, Packet}).
|
||||
|
||||
recv({http_poll, FsmRef}, _Length, Timeout) ->
|
||||
gen_fsm:sync_send_all_state_event(FsmRef, recv, Timeout).
|
||||
|
||||
close({http_poll, FsmRef}) ->
|
||||
catch gen_fsm:sync_send_all_state_event(FsmRef, close).
|
||||
|
||||
|
||||
process_request(#request{path = [],
|
||||
data = Data} = Request) ->
|
||||
case catch parse_request(Data) of
|
||||
{ok, ID1, Key, NewKey, Packet} ->
|
||||
ID = if
|
||||
(ID1 == "0") or (ID1 == "mobile") ->
|
||||
NewID = sha:sha(term_to_binary({now(), make_ref()})),
|
||||
{ok, Pid} = start(NewID, ""),
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
mnesia:write(#http_poll{id = NewID,
|
||||
pid = Pid})
|
||||
end),
|
||||
NewID;
|
||||
true ->
|
||||
ID1
|
||||
end,
|
||||
case http_put(ID, Key, NewKey, Packet) of
|
||||
{error, not_exists} ->
|
||||
{200, ?BAD_REQUEST, ""};
|
||||
{error, bad_key} ->
|
||||
{200, ?BAD_REQUEST, ""};
|
||||
ok ->
|
||||
receive
|
||||
after 100 -> ok
|
||||
end,
|
||||
case http_get(ID) of
|
||||
{error, not_exists} ->
|
||||
{200, [?BAD_REQUEST], ""};
|
||||
{ok, OutPacket} ->
|
||||
if
|
||||
ID == ID1 ->
|
||||
Cookie = "ID=" ++ ID ++ "; expires=-1",
|
||||
{200, [?CT, {"Set-Cookie", Cookie}],
|
||||
OutPacket};
|
||||
ID1 == "mobile" ->
|
||||
{200, [?CT], [ID, $\n, OutPacket]};
|
||||
true ->
|
||||
Cookie = "ID=" ++ ID ++ "; expires=-1",
|
||||
{200, [?CT, {"Set-Cookie", Cookie}],
|
||||
OutPacket}
|
||||
end
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
{200, [?CT, {"Set-Cookie", "ID=-2:0; expires=-1"}], ""}
|
||||
end;
|
||||
process_request(_Request) ->
|
||||
{400, [], {xmlelement, "h1", [],
|
||||
[{xmlcdata, "400 Bad Request"}]}}.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([ID, Key]) ->
|
||||
?INFO_MSG("started: ~p", [{ID, Key}]),
|
||||
Opts = [], % TODO
|
||||
ejabberd_c2s:start({?MODULE, {http_poll, self()}}, Opts),
|
||||
Timer = erlang:start_timer(?HTTP_POLL_TIMEOUT, self(), []),
|
||||
{ok, loop, #state{id = ID,
|
||||
key = Key,
|
||||
timer = Timer}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
%state_name(Event, From, StateData) ->
|
||||
% Reply = ok,
|
||||
% {reply, Reply, state_name, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event({send, Packet}, From, StateName, StateData) ->
|
||||
Output = [StateData#state.output | Packet],
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData#state{output = Output}};
|
||||
|
||||
handle_sync_event(recv, From, StateName, StateData) ->
|
||||
case StateData#state.input of
|
||||
"" ->
|
||||
{next_state, StateName, StateData#state{waiting_input = From}};
|
||||
Input ->
|
||||
Reply = {ok, list_to_binary(Input)},
|
||||
{reply, Reply, StateName, StateData#state{input = "",
|
||||
waiting_input = false}}
|
||||
end;
|
||||
|
||||
handle_sync_event(stop, From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{stop, normal, Reply, StateData};
|
||||
|
||||
handle_sync_event({http_put, Key, NewKey, Packet},
|
||||
From, StateName, StateData) ->
|
||||
Allow = case StateData#state.key of
|
||||
"" ->
|
||||
true;
|
||||
OldKey ->
|
||||
NextKey = jlib:encode_base64(
|
||||
binary_to_list(crypto:sha(Key))),
|
||||
if
|
||||
OldKey == NextKey ->
|
||||
true;
|
||||
true ->
|
||||
false
|
||||
end
|
||||
end,
|
||||
if
|
||||
Allow ->
|
||||
case StateData#state.waiting_input of
|
||||
false ->
|
||||
Input = [StateData#state.input | Packet],
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData#state{input = Input,
|
||||
key = NewKey}};
|
||||
Receiver ->
|
||||
gen_fsm:reply(Receiver, {ok, list_to_binary(Packet)}),
|
||||
cancel_timer(StateData#state.timer),
|
||||
Timer = erlang:start_timer(?HTTP_POLL_TIMEOUT, self(), []),
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName,
|
||||
StateData#state{waiting_input = false,
|
||||
key = NewKey,
|
||||
timer = Timer}}
|
||||
end;
|
||||
true ->
|
||||
Reply = {error, bad_key},
|
||||
{reply, Reply, StateName, StateData}
|
||||
end;
|
||||
|
||||
handle_sync_event(http_get, From, StateName, StateData) ->
|
||||
Reply = {ok, StateData#state.output},
|
||||
{reply, Reply, StateName, StateData#state{output = ""}};
|
||||
|
||||
handle_sync_event(Event, From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData}.
|
||||
|
||||
code_change(OldVsn, StateName, StateData, Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({timeout, Timer, _}, StateName,
|
||||
#state{timer = Timer} = StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StateData) ->
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
mnesia:delete({http_poll, StateData#state.id})
|
||||
end),
|
||||
case StateData#state.waiting_input of
|
||||
false ->
|
||||
ok;
|
||||
Receiver ->
|
||||
gen_fsm:reply(Receiver, {error, closed})
|
||||
end,
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
|
||||
http_put(ID, Key, NewKey, Packet) ->
|
||||
case mnesia:dirty_read({http_poll, ID}) of
|
||||
[] ->
|
||||
{error, not_exists};
|
||||
[#http_poll{pid = FsmRef}] ->
|
||||
gen_fsm:sync_send_all_state_event(
|
||||
FsmRef, {http_put, Key, NewKey, Packet})
|
||||
end.
|
||||
|
||||
http_get(ID) ->
|
||||
case mnesia:dirty_read({http_poll, ID}) of
|
||||
[] ->
|
||||
{error, not_exists};
|
||||
[#http_poll{pid = FsmRef}] ->
|
||||
gen_fsm:sync_send_all_state_event(FsmRef, http_get)
|
||||
end.
|
||||
|
||||
|
||||
parse_request(Data) ->
|
||||
Comma = string:chr(Data, $,),
|
||||
Header = lists:sublist(Data, Comma - 1),
|
||||
Packet = lists:nthtail(Comma, Data),
|
||||
{ID, Key, NewKey} =
|
||||
case string:tokens(Header, ";") of
|
||||
[ID1] ->
|
||||
{ID1, "", ""};
|
||||
[ID1, Key1] ->
|
||||
{ID1, Key1, Key1};
|
||||
[ID1, Key1, NewKey1] ->
|
||||
{ID1, Key1, NewKey1}
|
||||
end,
|
||||
{ok, ID, Key, NewKey, Packet}.
|
||||
|
||||
|
||||
cancel_timer(Timer) ->
|
||||
erlang:cancel_timer(Timer),
|
||||
receive
|
||||
{timeout, Timer, _} ->
|
||||
ok
|
||||
after 0 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_web.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 28 Feb 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_web).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([make_xhtml/1,
|
||||
process_get/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
|
||||
|
||||
make_xhtml(Els) ->
|
||||
{xmlelement, "html", [{"xmlns", "http://www.w3.org/1999/xhtml"},
|
||||
{"xml:lang", "en"},
|
||||
{"lang", "en"}],
|
||||
[{xmlelement, "head", [],
|
||||
[{xmlelement, "meta", [{"http-equiv", "Content-Type"},
|
||||
{"content", "text/html; charset=utf-8"}], []}]},
|
||||
{xmlelement, "body", [], Els}
|
||||
]}.
|
||||
|
||||
|
||||
-define(X(Name), {xmlelement, Name, [], []}).
|
||||
-define(XA(Name, Attrs), {xmlelement, Name, Attrs, []}).
|
||||
-define(XE(Name, Els), {xmlelement, Name, [], Els}).
|
||||
-define(XAE(Name, Attrs, Els), {xmlelement, Name, Attrs, Els}).
|
||||
-define(C(Text), {xmlcdata, Text}).
|
||||
-define(XC(Name, Text), ?XE(Name, [?C(Text)])).
|
||||
-define(XAC(Name, Attrs, Text), ?XAE(Name, Attrs, [?C(Text)])).
|
||||
|
||||
-define(LI(Els), ?XE("li", Els)).
|
||||
-define(A(URL, Els), ?XAE("a", [{"href", URL}], Els)).
|
||||
-define(AC(URL, Text), ?A(URL, [?C(Text)])).
|
||||
-define(P, ?X("p")).
|
||||
-define(BR, ?X("br")).
|
||||
-define(INPUT(Type, Name, Value),
|
||||
?XA("input", [{"type", Type},
|
||||
{"name", Name},
|
||||
{"value", Value}])).
|
||||
|
||||
|
||||
process_get({_, true},
|
||||
#request{user = User,
|
||||
path = ["admin" | RPath],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
if
|
||||
User /= undefined ->
|
||||
case acl:match_rule(configure, jlib:make_jid(User, ?MYNAME, "")) of
|
||||
deny ->
|
||||
{401, [], make_xhtml([?XC("h1", "Not Allowed")])};
|
||||
allow ->
|
||||
ejabberd_web_admin:process_admin(
|
||||
Request#request{path = RPath})
|
||||
end;
|
||||
true ->
|
||||
{401,
|
||||
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||
[{xmlcdata, "401 Unauthorized"}]}])}
|
||||
end;
|
||||
|
||||
process_get({true, _},
|
||||
#request{user = User,
|
||||
path = ["http-poll" | RPath],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
ejabberd_http_poll:process_request(Request#request{path = RPath});
|
||||
|
||||
process_get(_, _Request) ->
|
||||
{404, [], make_xhtml([?XC("h1", "Not found")])}.
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
[Settings]
|
||||
NumFields=2
|
||||
|
||||
[Field 1]
|
||||
Type=label
|
||||
Left=0
|
||||
Right=-1
|
||||
Top=10
|
||||
Bottom=70
|
||||
Text="Erlang OTP R9C-1 (version 5.3.6.2) or newer is required to install Ejabberd.\r\n\r\nIt is not found on your computer.\r\n\r\nPlease install Erlang OTP R9C-1 or newer before installing Ejabberd.\r\n\r\nIt can be downloaded from"
|
||||
|
||||
[Field 2]
|
||||
Type=link
|
||||
Left=0
|
||||
Right=-1
|
||||
Top=74
|
||||
Bottom=88
|
||||
State=http://www.erlang.org/download.html
|
||||
Text=http://www.erlang.org/download.html
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[Settings]
|
||||
NumFields=3
|
||||
|
||||
[Field 1]
|
||||
Type=label
|
||||
Left=0
|
||||
Right=-1
|
||||
Top=10
|
||||
Bottom=70
|
||||
Text="OpenSLL 0.9.7c or newer is not found on your computer.\r\n\r\nEjabberd includes required SSL libraries but if you don't have SSL certificate then without installed OpenSSL you will not be able to create your own SSL certificate and use SSL-enabled connections.\r\n\r\nOpenSLL can be downloaded from"
|
||||
|
||||
[Field 2]
|
||||
Type=link
|
||||
Left=0
|
||||
Right=-1
|
||||
Top=74
|
||||
Bottom=88
|
||||
State=http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
Text=http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
|
||||
[Field 3]
|
||||
Type=label
|
||||
Left=0
|
||||
Right=-1
|
||||
Top=93
|
||||
Bottom=-10
|
||||
Text="If you want to continue installation anyway, click Next."
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user