Compare commits

..

106 Commits

Author SHA1 Message Date
Alexey Shchepin e63b4afffd Added release tags
SVN Revision: 306
2005-04-17 00:21:31 +00:00
Alexey Shchepin e9f6529ead * src/tls/Makefile.win32: Added (thanks to Sergei Golovan)
* src/win32/: Updated (thanks to Sergei Golovan)

SVN Revision: 281
2004-10-10 17:15:24 +00:00
Alexey Shchepin e852a3c868 * src/msgs/uk.msg: Updated (thanks to Sergei Golovan)
SVN Revision: 280
2004-10-09 20:52:58 +00:00
Alexey Shchepin 4c7d56f462 * src/msgs/uk.msg: Updated (thanks to Sergei Golovan)
SVN Revision: 279
2004-10-09 20:42:42 +00:00
Alexey Shchepin 8ec7e54128 *** empty log message ***
SVN Revision: 278
2004-10-09 20:19:19 +00:00
Alexey Shchepin eeb109e75a * src/msgs/nl.msg: Updated (thanks to Sander Devrieze)
* src/msgs/ua.msg: Renamed to uk.msg

* COPYING: Added permission to link with OpenSSL

SVN Revision: 277
2004-10-09 20:15:49 +00:00
Alexey Shchepin 576db30b34 * src/msgs/ua.msg: Updated (thanks to Sergei Golovan)
SVN Revision: 276
2004-10-08 21:39:10 +00:00
Alexey Shchepin 134dec5dbb * 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)

SVN Revision: 275
2004-10-08 20:40:29 +00:00
Alexey Shchepin f327b06ed7 * doc/guide.tex: Updated
SVN Revision: 274
2004-10-06 15:07:21 +00:00
Alexey Shchepin 379ba26e85 * src/ejabberd_s2s_out.erl: Fixed socket closing condition
SVN Revision: 273
2004-10-06 14:12:04 +00:00
Alexey Shchepin b5e8850de9 * src/web/ejabberd_http_poll.erl: Properly handle bad requests
* src/web/ejabberd_web_admin.erl: Ported features from J-EAI

SVN Revision: 272
2004-10-05 19:31:17 +00:00
Alexey Shchepin 34de660c44 * 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)

SVN Revision: 271
2004-09-30 21:54:39 +00:00
Alexey Shchepin db824bfc64 * src/ejabberd_listener.erl: Check result of controlling_process
SVN Revision: 270
2004-09-29 21:10:40 +00:00
Alexey Shchepin 0e2af703bd * src/web/ejabberd_http.erl: Bugfix
SVN Revision: 269
2004-09-29 14:09:02 +00:00
Alexey Shchepin f17f0dac12 * src/Makefile.in: Updated (thanks to Badlop)
SVN Revision: 268
2004-09-27 18:54:17 +00:00
Alexey Shchepin cbcb2bd3a6 * src/aclocal.m4: Better expat and openssl detection (thanks to
Anton Vanin)

* src/**/Makefile.in: Updated (thanks to Anton Vanin)
* src/configure.ac: Likewise

SVN Revision: 267
2004-09-26 20:46:43 +00:00
Alexey Shchepin 4e7e67c8b5 * 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 from the
command line (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

SVN Revision: 266
2004-09-25 20:52:20 +00:00
Alexey Shchepin 6db9c2630d * 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)

* ejabberd/src/mod_pubsub/mod_pubsub.erl: Bugfix (thanks to
Mickael Remond)

* src/mod_pubsub/mod_pubsub.erl: Bugfix

SVN Revision: 265
2004-09-17 19:52:59 +00:00
Alexey Shchepin e0ede61e0f * 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)

SVN Revision: 264
2004-09-10 20:57:00 +00:00
Alexey Shchepin 210a9a689b * doc/guide.tex: Fix (thanks to Sander Devrieze)
SVN Revision: 263
2004-08-27 21:02:59 +00:00
Alexey Shchepin 1f3eb68e48 * src/xml_stream.erl: Few optimizations
SVN Revision: 262
2004-08-26 21:47:33 +00:00
Alexey Shchepin 6755925676 * src/mod_service_log.erl: Support for logging of user packets via
external service (e.g. bandersnatch)
* doc/guide.tex: Updated

SVN Revision: 261
2004-08-24 20:11:01 +00:00
Alexey Shchepin d543a63e9b * src/mod_offline.erl: Use offline_subscription_hook
SVN Revision: 260
2004-08-23 20:47:15 +00:00
Alexey Shchepin 4103f30812 * 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)

SVN Revision: 259
2004-08-22 21:54:14 +00:00
Alexey Shchepin ea322cd493 * src/msgs/nl.msg: Updated (thanks to Sander Devrieze)
SVN Revision: 258
2004-08-15 22:02:14 +00:00
Alexey Shchepin 69225f9e14 * src/web/ejabberd_http_poll.erl: Fixed sending of Set-Cookie
header

SVN Revision: 257
2004-08-15 21:18:53 +00:00
Alexey Shchepin d77a936b32 * 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)

SVN Revision: 256
2004-08-12 22:34:19 +00:00
Alexey Shchepin 357554265e * 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)

SVN Revision: 255
2004-08-08 19:07:55 +00:00
Alexey Shchepin 0ef537cc6c * 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

SVN Revision: 254
2004-08-05 20:46:24 +00:00
Alexey Shchepin a8f4e84503 * src/web/ejabberd_web_admin.erl: Added user's roster page
* src/mod_irc/mod_irc_connection.erl: Bugfix

SVN Revision: 253
2004-08-02 22:17:05 +00:00
Alexey Shchepin 73d7e1161e * 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

SVN Revision: 252
2004-08-01 20:12:20 +00:00
Alexey Shchepin 6cd02b9714 * 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)

SVN Revision: 251
2004-07-30 21:09:55 +00:00
Alexey Shchepin 76f00658f2 * 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

SVN Revision: 250
2004-07-28 20:08:53 +00:00
Alexey Shchepin e10fc255aa * src/tls/tls.erl: Updated
SVN Revision: 249
2004-07-26 22:37:24 +00:00
Alexey Shchepin 3377c54ecd *** empty log message ***
SVN Revision: 248
2004-07-26 16:45:38 +00:00
Alexey Shchepin 4b2919c207 * 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)

SVN Revision: 247
2004-07-25 21:27:56 +00:00
Alexey Shchepin 96cfe08558 * src/eldap/eldap.erl: Bugfix
SVN Revision: 246
2004-07-23 14:24:09 +00:00
Alexey Shchepin a4b86c5855 * 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)

SVN Revision: 245
2004-07-13 11:28:28 +00:00
Alexey Shchepin a846fe815c * 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)

SVN Revision: 244
2004-07-11 20:51:54 +00:00
Alexey Shchepin b64cc8f662 * src/mod_roster.erl: Removed superfluous include_lib line
SVN Revision: 243
2004-07-10 20:47:06 +00:00
Alexey Shchepin 6251519591 * doc/guide.tex: Updated
SVN Revision: 242
2004-07-10 20:06:27 +00:00
Alexey Shchepin d2eab0d535 * 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)

* src/win32/ejabberd.cfg: Updated (thanks to Sergei Golovan)

SVN Revision: 241
2004-07-09 22:34:26 +00:00
Alexey Shchepin b88da95b51 * 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

* doc/guide.tex: Updated (thanks to Sergei Golovan)

SVN Revision: 240
2004-07-06 21:34:50 +00:00
Alexey Shchepin d35209561d * src/ejabberd_auth.erl: Minor fix
* src/ejabberd_c2s.erl: Fixed sending of presence to own resources

SVN Revision: 239
2004-07-05 21:51:23 +00:00
Alexey Shchepin 84c4d93ca5 * 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

SVN Revision: 238
2004-06-17 21:29:24 +00:00
Alexey Shchepin ab8be3658f * src/msgs/nl.msg: Dutch translation (thanks to Sander Devrieze)
SVN Revision: 237
2004-05-22 20:15:22 +00:00
Alexey Shchepin 0b4539caa1 * 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

SVN Revision: 236
2004-05-22 19:48:35 +00:00
Alexey Shchepin 78a673f526 * 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

SVN Revision: 235
2004-05-17 20:36:41 +00:00
Alexey Shchepin d46abc7ad4 * src/web/ejabberd_web_admin.erl: Updated
SVN Revision: 234
2004-05-16 20:13:38 +00:00
Alexey Shchepin 0ef7655697 * src/web/ejabberd_web_admin.erl: Updated
SVN Revision: 233
2004-05-14 14:46:53 +00:00
Alexey Shchepin 63feb5dabf * src/web/ejabberd_web_admin.erl: Updated
* src/ejabberd_listener.erl: Added API for adding/removing
listeners

SVN Revision: 232
2004-05-09 18:38:49 +00:00
Alexey Shchepin c812a59efc * 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

SVN Revision: 231
2004-05-08 19:30:38 +00:00
Alexey Shchepin 964e61143c * src/web/ejabberd_web_admin.erl: Updated (thanks to Andrey
Zamaraev)

SVN Revision: 230
2004-05-07 15:19:51 +00:00
Alexey Shchepin f7275fb796 * 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)

SVN Revision: 229
2004-05-05 20:34:54 +00:00
Alexey Shchepin a16524151c * src/web/ejabberd_web_admin.erl: Updated
SVN Revision: 228
2004-05-04 19:47:24 +00:00
Alexey Shchepin 4760ff3201 * 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

SVN Revision: 227
2004-05-01 20:10:25 +00:00
Alexey Shchepin 13f650037e * 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)

SVN Revision: 226
2004-04-27 20:28:23 +00:00
Alexey Shchepin aaccaa5a45 * 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

SVN Revision: 225
2004-04-26 15:38:07 +00:00
Alexey Shchepin 0ab31f0822 * src/web/ejabberd_http.erl: Increased receive buffer
* src/mod_irc/mod_irc_connection.erl: Support for "/quote" command

SVN Revision: 224
2004-04-17 19:00:10 +00:00
Alexey Shchepin b542249f11 * 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)

SVN Revision: 223
2004-04-15 19:55:38 +00:00
Alexey Shchepin 6f600f1a37 * src/idna.erl: Support for IDNA (RFC3490)
* src/ejabberd_s2s_out.erl: Likewise

* 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

SVN Revision: 222
2004-04-10 19:15:02 +00:00
Alexey Shchepin b75fb79677 * (all): Updated win32 stuff (thanks to Sergei Golovan)
* src/web/ejabberd_web.erl: Added interface for access rules
configuration

SVN Revision: 221
2004-03-21 20:27:09 +00:00
Alexey Shchepin 2f47b7edd9 * doc/guide.tex: Updated
* src/web/ejabberd_web.erl: Updated

* src/web/ejabberd_http.erl: Bugfix

SVN Revision: 220
2004-03-20 20:55:58 +00:00
Alexey Shchepin 9e3c4edd1d * src/mod_roster.erl: Bugfix
SVN Revision: 219
2004-03-16 21:09:11 +00:00
Alexey Shchepin 90e48213e0 * src/ejabberd_s2s.erl: More verbose error handling
SVN Revision: 218
2004-03-16 14:34:20 +00:00
Alexey Shchepin f9f33a9117 * src/web/ejabberd_web.erl: Minor update
SVN Revision: 217
2004-03-15 20:46:42 +00:00
Alexey Shchepin 60cb9fc959 * src/web/ejabberd_web.erl: Added interface for users listsing and
statistics

SVN Revision: 216
2004-03-14 20:47:15 +00:00
Alexey Shchepin b4798f76a3 * 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

SVN Revision: 215
2004-03-13 20:01:37 +00:00
Alexey Shchepin a2b2d08ddc * src/web/ejabberd_web.erl: Experiments with web-interface
SVN Revision: 214
2004-03-12 15:15:17 +00:00
Alexey Shchepin 20e297db3a * src/configure.ac: Updated
* src/Makefile.in: Likewise

SVN Revision: 213
2004-03-12 13:36:27 +00:00
Alexey Shchepin b11b8caf9d * src/web/ejabberd_http.erl: Removed debugging output
* src/ejabberd_c2s.erl: Fixed processing of get_presence request
(thanks to Mickael Remond)

SVN Revision: 212
2004-03-10 21:54:54 +00:00
Alexey Shchepin bc672b44ea * 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)

SVN Revision: 211
2004-03-08 20:20:15 +00:00
Alexey Shchepin 36f90d6a2d * src/web/ejabberd_http_poll.erl: Completed
SVN Revision: 210
2004-03-07 21:15:12 +00:00
Alexey Shchepin a0ae476619 * src/web/: Support for HTTP Polling (JEP-0025) (almost complete)
SVN Revision: 209
2004-03-06 21:13:16 +00:00
Alexey Shchepin 5696c848a6 * src/web/: Updated
SVN Revision: 208
2004-03-04 20:56:32 +00:00
Alexey Shchepin 0f95685c05 * src/web/: Minor update
SVN Revision: 207
2004-03-03 21:07:27 +00:00
Alexey Shchepin ec182cbd60 * 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)

SVN Revision: 206
2004-03-02 21:16:55 +00:00
Alexey Shchepin 21c4b65610 * 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)

* src/ejabberd_sm.erl: Bugfix

SVN Revision: 205
2004-02-26 22:00:04 +00:00
Alexey Shchepin fdf25720e0 * 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
"ejabber:config" namespace (thanks to Sergei Golovan)
* src/mod_disco.erl: Likewise

SVN Revision: 204
2004-02-15 20:10:40 +00:00
Alexey Shchepin 35bfd03669 * src/ejabberd_c2s.erl: Added <session/> to stream features
SVN Revision: 203
2004-02-12 11:26:58 +00:00
Alexey Shchepin b0b4b9a903 * src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
* src/mod_irc/mod_irc.erl: Now uses "ejabber: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 "ejabber:config" namespace (thanks to Sergei
Golovan)

SVN Revision: 202
2004-02-10 20:50:41 +00:00
Alexey Shchepin 976e185a4a * src/ejabberd_ctl.erl: Added command for log reopening
SVN Revision: 201
2004-01-27 19:14:49 +00:00
Alexey Shchepin 273886701b * 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

SVN Revision: 200
2004-01-18 20:42:09 +00:00
Alexey Shchepin 7a002470e6 * 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)

SVN Revision: 199
2004-01-17 20:26:57 +00:00
Alexey Shchepin c7d15b12df * 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

SVN Revision: 198
2004-01-11 20:42:57 +00:00
Alexey Shchepin 41cc38aa6f * src/ejabberd_router.erl: Added monitoring of processess that
serve domains

SVN Revision: 197
2004-01-06 20:37:14 +00:00
Alexey Shchepin 7f9b7e64c3 * src/ejabberd_app.erl: Bugfix
SVN Revision: 196
2004-01-06 12:03:39 +00:00
Alexey Shchepin 5d7f5c3a79 * src/ejabberd_router.erl (do_route/3): Slightly changed behaviour
SVN Revision: 195
2004-01-03 17:25:21 +00:00
Alexey Shchepin 7e6d96b192 * src/ejabberd_sm.erl (do_route/3): Minor fix
* src/ejabberd_sm.erl (route_message/3): Minor changes

SVN Revision: 194
2004-01-02 19:36:38 +00:00
Alexey Shchepin 1d1d6b6c36 * 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

SVN Revision: 193
2003-12-28 21:12:31 +00:00
Alexey Shchepin 91aeaae9a5 * 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

SVN Revision: 192
2003-12-28 20:59:21 +00:00
Alexey Shchepin a22d1851cc * 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

SVN Revision: 191
2003-12-28 20:46:11 +00:00
Alexey Shchepin fc4bf57f95 * 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

SVN Revision: 190
2003-12-28 20:33:01 +00:00
Alexey Shchepin 8076bc8999 * src/ejabberd_c2s.erl: Presence probe now sended from full JID
* src/mod_roster.erl: Bugfix

SVN Revision: 189
2003-12-24 21:05:45 +00:00
Alexey Shchepin 1b76800448 * src/ejabberd_c2s.erl: Bugfix
SVN Revision: 188
2003-12-23 11:33:31 +00:00
Alexey Shchepin 6706bdc696 * src/mod_roster.erl: Added workaround for legacy gateways
(passing of "subscribed" presence)

* src/ejabberd_sm.erl: Minor fix in subscription processing

SVN Revision: 187
2003-12-21 18:16:08 +00:00
Alexey Shchepin b3b09bcfd8 * src/jlib.hrl: Added declaration of "iq" record
* (all): Updated to use "iq" record

SVN Revision: 186
2003-12-17 20:13:21 +00:00
Alexey Shchepin 4768cd2f26 * 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

SVN Revision: 185
2003-12-14 20:51:01 +00:00
Alexey Shchepin 4413036585 * src/ejabberd_s2s.erl: Partially rewrited
* src/mod_roster.erl: Removed debugging code

SVN Revision: 184
2003-12-13 21:36:53 +00:00
Alexey Shchepin 91fe973361 * src/ejabberd_s2s_out.erl: Bugfix
SVN Revision: 183
2003-12-12 20:35:40 +00:00
Alexey Shchepin 8b5c042dee * src/mod_roster.erl: Updated subscription handling to latest
XMPP-IM draft

SVN Revision: 182
2003-12-11 20:31:40 +00:00
Alexey Shchepin 6f9a3922ef * 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)

SVN Revision: 181
2003-12-06 19:58:49 +00:00
Alexey Shchepin 83a0be1009 * src/ejabberd_c2s.erl: Bugfix in processing of connection
replacement

SVN Revision: 180
2003-12-02 20:20:53 +00:00
Alexey Shchepin 48e8a0c0c6 * src/ejabberd_c2s.erl: Added workaround for some Java clients
SVN Revision: 179
2003-11-28 19:08:51 +00:00
Alexey Shchepin b154f75523 * 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

SVN Revision: 178
2003-11-27 20:16:10 +00:00
Alexey Shchepin 9fc8e18eb3 * src/ejabberd_c2s.erl: Fixed processing of presence probe from
client

SVN Revision: 177
2003-11-26 19:18:42 +00:00
109 changed files with 14594 additions and 3268 deletions
+3
View File
@@ -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
+883
View File
@@ -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)
+3
View File
@@ -1,3 +1,6 @@
admin tool
mod_muc logging
admin interface
users management
statistics about each user
+786 -416
View File
File diff suppressed because it is too large Load Diff
+814 -431
View File
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

+50
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+38 -8
View File
@@ -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/>
+269 -14
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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().
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+231
View File
@@ -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"
).
+183
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+4 -1
View File
@@ -15,7 +15,7 @@
%% gen_event callbacks
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
code_change/3]).
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
%%%----------------------------------------------------------------------
+31 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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]}}.
+9 -7
View File
@@ -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
+27
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+76
View File
@@ -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.
+11
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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}).
+200
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+1 -1
View File
@@ -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}),
+2 -4
View File
@@ -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
View File
@@ -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;
+78 -61
View File
@@ -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
View File
@@ -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.
+6 -9
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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;
File diff suppressed because it is too large Load Diff
+156 -13
View File
@@ -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
View File
@@ -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
View File
@@ -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] ->
+5 -8
View File
@@ -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
+1 -1
View File
@@ -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
+49 -43
View File
@@ -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
View File
@@ -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
View File
@@ -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() ->
+59
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+572
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+8 -17
View File
@@ -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
+16 -21
View File
@@ -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)
+1 -1
View File
@@ -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}),
+2 -4
View File
@@ -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;
}
+38
View File
@@ -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
+37
View File
@@ -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
View File
@@ -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.
+200
View File
@@ -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
View File
@@ -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([]) ->
[].
+34
View File
@@ -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
+28
View File
@@ -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
+822
View File
@@ -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]).
+17
View File
@@ -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 = ""
}).
+332
View File
@@ -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.
+83
View File
@@ -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
+20
View File
@@ -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
+28
View File
@@ -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