Compare commits

..

90 Commits

Author SHA1 Message Date
Mickaël Rémond de080352ca Release 1.1.1
SVN Revision: 559
2006-04-28 10:45:17 +00:00
Mickaël Rémond 719306c999 * doc/guide.html: Updated generated HTML version of the guide.
SVN Revision: 558
2006-04-28 09:38:44 +00:00
Mickaël Rémond b86e1a9e5b * src/ejabberd.hrl: release 1.1.1
* doc/version.tex: Likewise
* doc/guide.tex: Likewise

SVN Revision: 557
2006-04-28 09:35:26 +00:00
Mickaël Rémond 960e66423d * Release not for security fix release.
SVN Revision: 556
2006-04-28 08:43:15 +00:00
Alexey Shchepin 66149f862f * src/cyrsasl.erl: Bugfix: anonymous authentication was always
enabled

SVN Revision: 555
2006-04-27 21:24:30 +00:00
Mickaël Rémond bd4c18516c * src/ejabberd_ctl.erl: The status now only returns "started" if
ejabberd is started and ready to accept requests.

SVN Revision: 554
2006-04-26 22:28:05 +00:00
Mickaël Rémond d3777ba590 * Fixed notes for version 1.1.0 release.
SVN Revision: 551
2006-04-24 21:32:31 +00:00
Alexey Shchepin 3e3d78effc * doc/guide.tex: Added a documentation for max_stanza_size option
SVN Revision: 550
2006-04-24 19:25:09 +00:00
Mickaël Rémond 7483d337b4 * src/ejabberd.hrl: Updated for version 1.1.0.
SVN Revision: 549
2006-04-24 13:04:21 +00:00
Mickaël Rémond ccd28e0e5b * Updated documentation before 1.1.0 release
SVN Revision: 548
2006-04-23 22:00:06 +00:00
Mickaël Rémond f01dea67a0 * Updated documentation before 1.1.0 release
SVN Revision: 547
2006-04-23 21:47:18 +00:00
Mickaël Rémond 35c42e2592 doc/release_notes_1.1.0.txt: Preparing release 1.1.0
SVN Revision: 546
2006-04-23 20:43:40 +00:00
Mickaël Rémond 29cec06fea * src/guide.tex: Added documentation for ODBC and native database
configuration (PostgreSQL and MySQL).

SVN Revision: 545
2006-04-23 20:04:40 +00:00
Alexey Shchepin 957ddb325e * doc/guide.tex: Updated (thanks to Sander Devrieze)
* doc/introduction.tex: Likewise

SVN Revision: 544
2006-04-23 16:55:25 +00:00
Alexey Shchepin 1694a6a756 * src/configure: Removed '==' bashism (thanks to Magnus Henoch)
* src/aclocal.m4: Likewise

SVN Revision: 543
2006-04-23 15:35:14 +00:00
Alexey Shchepin 25ca5013bb * src/ejabberd_c2s.erl: Resend messages when leaving negative
presence state (thanks to Magnus Henoch)

SVN Revision: 542
2006-04-23 14:57:37 +00:00
Mickaël Rémond c86275ce9d * src/ejabberd_sm.erl: Now delivering messages to all resources with
max equal priority when this priority is non-negative.

SVN Revision: 541
2006-04-23 09:31:54 +00:00
Mickaël Rémond dc198cf08e * src/msgs/de.msg: Updated (thanks to Nikolaus Polak)
SVN Revision: 540
2006-04-23 09:26:41 +00:00
Mickaël Rémond d5309f0af4 * src/msgs/fr.msg: updated
SVN Revision: 539
2006-04-23 08:56:46 +00:00
Alexey Shchepin 8eb823abbb * src/msgs/pt-br.msg: Updated (thanks to Lucius Curado)
* src/xml_stream.erl: Support for stanza size limit (thanks to
Igor Goryachev)
* src/ejabberd_receiver.erl: Likewise
* src/ejabberd_c2s.erl: Likewise
* src/ejabberd_s2s_in.erl: Likewise
* src/ejabberd.cfg.example: Updated

* src/ejabberd_auth.erl: Fixed try_register/3 behaviour

SVN Revision: 538
2006-04-22 04:02:42 +00:00
Alexey Shchepin c4b976881e * src/msgs/pt-br.msg: Updated (thanks to Lucius Curado)
* src/xml_stream.erl: Support for stanza size limit (thanks to
Igor Goryachev)
* src/ejabberd_receiver.erl: Likewise
* src/ejabberd_c2s.erl: Likewise
* src/ejabberd_s2s_in.erl: Likewise
* src/ejabberd.cfg.example: Updated

* src/ejabberd_auth.erl: Fixed try_register/3 behaviour

SVN Revision: 537
2006-04-22 03:35:13 +00:00
Mickaël Rémond e0c700e725 * doc/guide.tex: Updated (SASL anonymous and anonymous login).
SVN Revision: 536
2006-04-20 22:12:09 +00:00
Mickaël Rémond 8688bc508f * src/ejabberd_auth.erl: Bugfix: We now are forced to check is an
anonymous user is log under a given user name before trying to register
it.

SVN Revision: 535
2006-04-20 17:51:29 +00:00
Mickaël Rémond 3cce56b9f8 * src/ejabberd.cfg.example: Update of the example for anonymous.
Removed unnecessary parameter.
* src/ejabberd_auth_anonymous.erl: Removed unnecessary parameter
and bugfix.

SVN Revision: 534
2006-04-20 15:42:51 +00:00
Alexey Shchepin 4797060eb9 * src/msgs/pl.msg: Updated (thanks to Andrzej Smyk)
SVN Revision: 533
2006-04-18 22:28:30 +00:00
Alexey Shchepin 15a714ae68 * src/xml_stream.erl: Cleanup
* src/ejabberd_service.erl: Updated to use ejabberd_receiver

SVN Revision: 532
2006-04-13 02:08:24 +00:00
Alexey Shchepin aa8e532cbd * src/ejabberd_c2s.erl: Minor fix
* src/ejabberd_c2s.erl: Bugfix (thanks to Sergei Golovan)

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

* src/msgs/nl.msg: Updated (thanks to Sander Devrieze)

SVN Revision: 531
2006-04-12 02:54:27 +00:00
Alexey Shchepin 681f0b9567 * src/msgs/es.msg: Updated (thanks to Badlop)
* src/ejabberd.cfg.example: Updated (thanks to Badlop)

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

SVN Revision: 530
2006-04-11 03:16:34 +00:00
Mickaël Rémond 49917e284e * src/mod_pubsub/mod_pubsub.erl: Fixed pubsub root node creation
(conflict error was send on node creation)

SVN Revision: 529
2006-04-07 13:39:48 +00:00
Alexey Shchepin 0e1f86e1f5 * src/ejabberd_sm.erl: SASL Anonymous + Anonymous login support
(thanks to Mickael Remond and Magnus Henoch)
* src/ejabberd_c2s.erl: Likewise
* src/ejabberd_auth.erl: Likewise
* src/ejabberd_auth_anonymous.erl: Likewise
* src/cyrsasl.erl: Likewise
* src/cyrsasl_anonymous.erl: Likewise
* src/ejabberd.cfg.example: Likewise

SVN Revision: 528
2006-04-07 00:51:53 +00:00
Alexey Shchepin 9a44fdffab * src/ejabberd_sm.erl: SASL Anonymous + Anonymous login support
(thanks to Mickael Remond and Magnus Henoch)
* src/ejabberd_c2s.erl: Likewise
* src/ejabberd_auth.erl: Likewise
* src/ejabberd_auth_anonymous.erl: Likewise
* src/cyrsasl.erl: Likewise
* src/cyrsasl_anonymous.erl: Likewise
* src/ejabberd.cfg.example: Likewise

SVN Revision: 527
2006-04-07 00:39:24 +00:00
Alexey Shchepin a5a978946d * src/expat_erl.c: Use binaries for CDATA
* src/xml.erl: Likewise

SVN Revision: 526
2006-04-05 23:56:16 +00:00
Alexey Shchepin ed43f8024c * src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
* src/msgs/uk.msg: Likewise

* src/mod_configure.erl: Updated translation strings (thanks to
Sergei Golovan)
* src/web/ejabberd_web_admin.erl: Likewise

* src/ejabberd_s2s_in.erl: Changed a path to PKIX includes (thanks
to Sergei Golovan)

SVN Revision: 525
2006-04-01 21:43:52 +00:00
Mickaël Rémond 3e9e51ee30 * src/Makefile.in: Added an option to compile ejabberd with debug
print-out (make ejabberd_debug=true).
* src/ejabberd_c2s.erl: Added a new debug printout: We can now
print the XML packets send by the server.

SVN Revision: 524
2006-04-01 16:05:20 +00:00
Alexey Shchepin f3085d09e4 * src/ejabberd_c2s.erl: Bugfix
SVN Revision: 523
2006-03-30 23:30:05 +00:00
Alexey Shchepin 832279f1f2 * src/ejabberd_c2s.erl: Enable zlib only over TCP
SVN Revision: 522
2006-03-25 21:16:49 +00:00
Alexey Shchepin 89e57d421e * src/ejabberd_c2s.erl: Bugfix (thanks to Sergei Golovan)
SVN Revision: 521
2006-03-18 20:10:56 +00:00
Alexey Shchepin a6b3e6c904 * src/mod_privacy.erl: Fixed privacy list pushing (thanks to
Sergei Golovan)
* src/ejabberd_c2s.erl: Likewise

SVN Revision: 520
2006-03-17 04:35:03 +00:00
Mickaël Rémond eabf8d1c6c * src/ejabberd_sm.erl: Remove unnecessary use delete_object,
which can, in some context break indexes, when used on non bag table.

SVN Revision: 519
2006-03-16 09:59:45 +00:00
Alexey Shchepin 7278a606a8 * src/ejabberd_c2s.erl: Bugfix
SVN Revision: 518
2006-03-16 01:02:44 +00:00
Alexey Shchepin d5137b0563 * src/xml_stream.erl: Added catching of gen_fsm:send_event errors
* src/ejabberd_s2s_out.erl: Better support for multiple SRV
records (thanks to Sergei Golovan)

* src/mod_muc/mod_muc_log.erl: Support for chatroom logging
(thanks to Badlop)
* src/mod_muc/mod_muc_room.erl: Likewise
* src/mod_muc/Makefile.in: Likewise
* src/mod_muc/Makefile.win32: Likewise

* src/gen_iq_handler.erl: Added support for {queues, N} IQ handler
type

SVN Revision: 517
2006-03-14 04:26:15 +00:00
Alexey Shchepin 4f2a355408 * src/mod_muc/mod_muc_room.erl: Bugfix
SVN Revision: 516
2006-03-06 02:30:15 +00:00
Alexey Shchepin d837cb9e8d * src/ejabberd_c2s.erl: Bugfix
* src/ejabberd_auth_odbc.erl: Bugfix

SVN Revision: 515
2006-03-05 04:55:43 +00:00
Mickaël Rémond 804c32c100 * src/ejabberd_logger_h.erl: reopen-log function now rename the log
file if it has not been already renamed by a logrotate process.  This
change allow ejabberd administrators to rotate log files on Windows
(EJAB-52).

SVN Revision: 514
2006-03-04 17:33:23 +00:00
Alexey Shchepin 17420e058b * src/web/ejabberd_web_admin.erl: Added a interface for node
updating
* src/ejabberd_update.erl: Added function update_info/0

SVN Revision: 513
2006-02-27 04:47:30 +00:00
Alexey Shchepin 20d049fe28 * src/web/ejabberd_web_admin.erl: Added a interface for node
updating
* src/ejabberd_update.erl: Added function update_info/0

SVN Revision: 512
2006-02-27 04:43:16 +00:00
Alexey Shchepin 51c4ab8351 * src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
* src/msgs/uk.msg: Likewise

* contrib/extract_translations/extract_translations.erl: Minor
update (thanks to Sergei Golovan)

* src/ejabberd_zlib/Makefile.win32: Zlib support for Windows build
(thanks to Sergei Golovan)
* src/Makefile.win32: Likewise
* src/configure.erl: Likewise

SVN Revision: 511
2006-02-25 19:48:17 +00:00
Alexey Shchepin 80181dc618 * src/ejabberd_ctl.erl: Added API for virtual host specific
commands, removed registered-users command
* src/ejabberd_auth.erl: Added processing of registered-users
command
* src/ejabberd_auth_internal.erl: Likewise
* src/ejabberd_auth_ldap.erl: Likewise
* src/ejabberd_auth_odbc.erl: Likewise
* src/ejabberd_app.erl: Added inititalization of ejabberd_ctl
* src/ejabberd_sm.erl: Minor update

SVN Revision: 510
2006-02-20 04:07:42 +00:00
Alexey Shchepin 0fd01cfff8 * src/mod_irc/mod_irc_connection.erl: Added handling for "%", "&"
and "~" prefixes in IRC nicknames (thanks to Vladimir Kulev)

SVN Revision: 509
2006-02-18 20:05:05 +00:00
Alexey Shchepin 44fdd72041 * src/mod_irc/mod_irc_connection.erl: Added /msg and /ctcp
commands, improved handling of channel topic and kick, mirc colors
now filtered, other minor improvements (thanks to Oleg
V. Motienko)

* src/mod_configure.erl: Improved strings (thanks to Sander
Devrieze)
* src/mod_vcard.erl: Likewise
* src/mod_vcard_ldap.erl: Likewise
* src/mod_vcard_odbc.erl: Likewise
* src/web/ejabberd_web_admin.erl: Likewise

SVN Revision: 508
2006-02-18 19:56:16 +00:00
Alexey Shchepin 0234b83be4 * src/mod_service_log.erl: Bugfix (thanks to Badlop)
* src/jd2ejd.erl: Use mod_vcard_odbc if it is loaded (thanks to
Tomasz Sterna)

* src/ejabberd_sm.erl: Added a few ejabberdctl commands

* src/ejabberd_ctl.erl: New interface for command registration

SVN Revision: 507
2006-02-15 04:15:54 +00:00
Alexey Shchepin 904300ce04 * src/ejabberd_ctl.erl: Partially rewritten
* src/ejabberd_ctl.hrl: Definitions of exit status codes moved
here

SVN Revision: 506
2006-02-13 04:05:13 +00:00
Alexey Shchepin 3eb1b59e64 * src/ejabberd_ctl.erl: Partially rewritten
SVN Revision: 505
2006-02-13 04:02:59 +00:00
Alexey Shchepin 73073d4af5 * src/mod_roster_odbc.erl: Bugfix
SVN Revision: 504
2006-02-12 18:40:03 +00:00
Alexey Shchepin 6eac737807 * src/mod_roster_odbc.erl: Bugfix
SVN Revision: 503
2006-02-11 23:55:21 +00:00
Alexey Shchepin b8e99a3c05 * src/odbc/ejabberd_odbc.erl: Reconnect on ODBC connection closing
SVN Revision: 502
2006-02-08 03:55:30 +00:00
Mickaël Rémond 2f0cf8bce8 * src/ejabberd_auth.erl: plugin authentication modules can now
be used in the configuration file: {auth_method, module} will
now use the module ejabberd_auth_module.erl for authentication.
(EJAB-45)

SVN Revision: 501
2006-02-07 17:35:32 +00:00
Alexey Shchepin 0cc168a9aa * src/mod_muc/mod_muc_room.erl: Kick non-members when room
reconfigured to be member-only or when membership is revoked in
member-only room, allowed "true" and "false" in x:data forms
(thanks to Sergei Golovan)

* src/mod_configure.erl: Added "Host" parameter to get_form and
set_form functions (thanks to Sergei Golovan)

* src/ejabberd_s2s_out.erl: Bugfix

* src/msgs/es.msg: Updated (thanks to Badlop)

* src/web/ejabberd_web.erl: Bugfix (thanks to Badlop)

* src/mod_irc/mod_irc.erl: Updated copyright dates
* src/mod_muc/mod_muc.erl: Likewise
* src/mod_pubsub/mod_pubsub.erl: Likewise
* src/mod_vcard.erl: Likewise
* src/mod_vcard_ldap.erl: Likewise
* src/mod_vcard_odbc.erl: Likewise
* src/web/ejabberd_web_admin.erl: Likewise

SVN Revision: 500
2006-02-06 05:12:54 +00:00
Alexey Shchepin cf10843071 * src/ejabberd_auth_odbc.erl: Added ejabberd_odbc_sup to ejabberd
supervision tree

SVN Revision: 499
2006-02-03 04:41:03 +00:00
Alexey Shchepin dc57e75e8f * src/web/ejabberd_http.erl: Authentication check moved to
ejabberd_web.erl
* src/web/ejabberd_web.erl: Likewise

* src/web/Makefile.in: Added ejabberd_http.hrl dependency

* src/web/ejabberd_http_poll.erl: Updated to use {active, once}
socket mode

* src/mod_irc/mod_irc.erl: Updated to use gen_server behaviour and
ejabberd supervision tree
* src/mod_irc/mod_irc_connection.erl: Likewise

SVN Revision: 498
2006-02-03 03:28:15 +00:00
Mickaël Rémond facefdb6e4 --prefix option can now override the default install dir (EJAB-43)
SVN Revision: 497
2006-02-02 16:59:38 +00:00
Alexey Shchepin 971001e7aa * src/mod_pubsub/mod_pubsub.erl: Updated to use gen_server
behaviour and ejabberd supervision tree

* src/mod_muc/mod_muc.erl: Added a supervisor for conference room
processes
* src/mod_muc/mod_muc_room.erl: Likewise

SVN Revision: 496
2006-02-02 05:00:27 +00:00
Alexey Shchepin 50f44530d2 * src/odbc/pg.sql: Fixed syntax error
* src/ejabberd_router.erl: Updated to use gen_server behaviour
* src/ejabberd_sm.erl: Likewise
* src/ejabberd_s2s.erl: Likewise
* src/gen_iq_handler.erl: Likewise

* src/ejabberd_sup.erl: Added supervisor for ejabberd_receiver
* src/ejabberd_receiver.erl: Updated

SVN Revision: 495
2006-01-29 04:38:31 +00:00
Alexey Shchepin ef456ab645 * src/ejabberd_update.erl: Support for run-time ejabberd updating
(not completed)
* src/ejabberd_c2s.erl: Added 'update_info' module attribute for
testing ejabberd_update

SVN Revision: 494
2006-01-27 05:02:33 +00:00
Alexey Shchepin 90618de7be * src/ejabberd_update.erl: Support for run-time ejabberd updating
(not completed)
* src/ejabberd_c2s.erl: Added 'update_info' module attribute for
testing ejabberd_update

SVN Revision: 493
2006-01-27 05:00:27 +00:00
Alexey Shchepin efacb0ad66 * src/ejabberd_c2s.erl: Remove top-level xmlns from incoming
stanzas
* src/ejabberd_s2s_in.erl: Likewise
* src/ejabberd_service.erl: Likewise

SVN Revision: 492
2006-01-25 00:35:12 +00:00
Alexey Shchepin 976a616662 * src/ejabberd_c2s.erl: Better error handling for stream
compression support (thanks to Sergei Golovan)

SVN Revision: 491
2006-01-24 22:52:23 +00:00
Alexey Shchepin ef0b3ab24c * src/mod_roster_odbc.erl: Bugfix
SVN Revision: 490
2006-01-24 21:47:53 +00:00
Alexey Shchepin 2d7fbf3964 Fixed ChangeLog
SVN Revision: 489
2006-01-24 02:55:44 +00:00
Alexey Shchepin a4caafeb72 * src/ejabberd_sm.erl: Partially rewritten to work more
efficiently and avoid race conditions
* src/ejabberd_c2s.erl: Likewise

* src/mod_irc/mod_irc_connection.erl: Cleanup

SVN Revision: 488
2006-01-23 23:13:06 +00:00
Mickaël Rémond 1295893864 * src/ejabberd_receiver.erl: Added new debugging trace: It is now
possible to dump the XML stream received from a client (usefull for
client debugging).

SVN Revision: 487
2006-01-20 16:51:58 +00:00
Alexey Shchepin 568909d5bb * src/aclocal.m4: Updated for zlib support
* src/configure.ac: Likewise

* src/mod_muc/mod_muc_room.erl: Weakened presence filtering, added
warning in non-anonymous rooms, room destroying updated to latest
JEP-0045, added a number of occupants and room name in room's
disco#info reply, miscellaneous internal changes (thanks to Sergei
Golovan)

* src/mod_muc/mod_muc.erl: Better support for nick unregistration
(thanks to Sergei Golovan)

* src/ejabberd_zlib/ejabberd_zlib.erl: Zlib support (thanks to
Sergei Golovan)
* src/ejabberd_zlib/ejabberd_zlib_drv.c: Likewise
* src/ejabberd_zlib/Makefile.in: Likewise
* src/ejabberd_c2s.erl: Stream compression support (JEP-0138)
* src/ejabberd_receiver.erl: Likewise

* src/mod_disco.erl: Don't split node name before calling hooks
(thanks to Sergei Golovan)

* src/mod_configure.erl: Support for configuration using ad-hoc
commands (thanks to Sergei Golovan)

* src/mod_announce.erl: Support for sending announce messages
using ad-hoc commands (thanks to Sergei Golovan)

* src/mod_adhoc.erl: Ad-hoc support (JEP-0050) (thanks to Magnus
Henoch)
* src/adhoc.erl: Likewise
* src/adhoc.hrl: Likewise

* src/jlib.hrl: Updated (thanks to Sergei Golovan)

* src/gen_mod.erl: Added function is_loaded/2 (thanks to Sergei
Golovan)

* src/ejabberd_service.erl: Changed error message on handshake
error (thanks to Sergei Golovan)

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

SVN Revision: 486
2006-01-19 02:17:31 +00:00
Mickaël Rémond c7bafe0056 * src/odbc/ejabberd_odbc.erl: Added a way to retry database
connection connect for 5 minutes when the connection is lost. No further
connection is retry after 5 minutes (EJAB-22 & EJAB-25)
* src/odbc/ejabberd_odbc_sup.erl: likewise

SVN Revision: 485
2006-01-13 18:05:10 +00:00
Mickaël Rémond 54a1ced6f6 * src/odbc/ejabberd_odbc.erl: underscore and percent are now only
escaped in like queries. MySQL where not escaping those escaped
characters in other context (EJAB-24)
* src/mod_vcard_odbc.erl: likewise.
* src/odbc/mysql.sql: Fixed MySQL database creation script: Was
not properly working with all MySQL version.

SVN Revision: 484
2006-01-13 10:59:52 +00:00
Alexey Shchepin 6bb510d99e * src/ejabberd_service.erl: Bugfix
* src/ejabberd_receiver.erl: Rewritten to use {active, once} mode
for socket
* src/ejabberd_c2s.erl: Update
* src/ejabberd_listener.erl: Likewise
* src/ejabberd_s2s_in.erl: Likewise
* src/ejabberd_s2s_out.erl: Likewise
* src/ejabberd_service.erl: Likewise
* src/shaper.erl: Likewise
* src/tls/tls.erl: Likewise
* src/web/ejabberd_http.erl: Likewise

SVN Revision: 483
2006-01-13 01:55:20 +00:00
Mickaël Rémond 8401a5ac55 Native MySQL support. The Erlang MySQL module is needed:
http://support.process-one.net/doc/display/CONTRIBS/Yxa

SVN Revision: 482
2006-01-02 17:39:04 +00:00
Alexey Shchepin 7ee3e45df1 * src/ejabberd_logger_h.erl: Speed optimizations
SVN Revision: 481
2005-12-24 02:40:35 +00:00
Mickaël Rémond 784edef4b6 Added missing file.
SVN Revision: 480
2005-12-23 13:52:39 +00:00
Alexey Shchepin 254a8c1b60 * src/Makefile.in: Clean results of ASN.1 compiler (thanks to
Sergei Golovan)

SVN Revision: 479
2005-12-22 14:48:01 +00:00
Alexey Shchepin 3cbed307bc * src/win32/ejabberd.nsh: Removed (thanks to Sergei Golovan)
SVN Revision: 478
2005-12-22 13:44:58 +00:00
Alexey Shchepin 08af86cb11 * src/Makefile.win32: Updated (thanks to Sergei Golovan)
* src/configure.erl: Likewise
* src/win32/CheckReqs.ini: Likewise
* src/win32/CheckReqs1.ini: Likewise
* src/win32/CheckReqs1H.ini: Likewise
* src/win32/ejabberd.nsh: Likewise
* src/win32/ejabberd.nsi: Likewise

SVN Revision: 477
2005-12-22 13:35:54 +00:00
Mickaël Rémond 15e747782a * src/odbc/ejabberd_odbc.erl: Added error message on ODBC
connection to help ODBC configuration troubleshooting.

SVN Revision: 476
2005-12-22 09:40:21 +00:00
Alexey Shchepin 0035c63399 * src/odbc/ejabberd_odbc.erl: Bugfix
SVN Revision: 475
2005-12-22 05:38:04 +00:00
Mickaël Rémond 559b5cae14 * src/odbc/mysql.sql: Database description for MySQL Database
4.1 (Max version, with Innodb)
* src/ejabberd_auth_odbc.erl: MySQL ODBC support.
* src/mod_offline_odbc.erl: likewise
* src/mod_vcard_odbc.erl: likewsie
* src/mod_roster_odbc.erl: likewise
* src/odbc/ejabberd_odbc.erl: likewise
* src/mod_last_odbc.erl: likewise

SVN Revision: 474
2005-12-21 16:10:56 +00:00
Mickaël Rémond c275a24597 Documentation update, code clean-up and Makefile improvement.
SVN Revision: 473
2005-12-16 16:33:42 +00:00
Alexey Shchepin 52fcdc716d * doc/features.tex: Added feature sheet (Thanks to Sander
Devrieze)

SVN Revision: 472
2005-12-15 23:31:49 +00:00
Mickaël Rémond 9064851ab5 Documentation updates for actual ejabberd 1.0.0 release.
SVN Revision: 471
2005-12-15 08:57:33 +00:00
Alexey Shchepin 5f2de53f01 * src/mod_irc/iconv_erl.c: Bugfix (thanks to Tomas Janousek)
SVN Revision: 470
2005-12-14 01:32:48 +00:00
Alexey Shchepin 689842edf8 * src/win32/ejabberd.nsi: Updated (thanks to Maxim Ryazanov)
SVN Revision: 468
2005-12-13 20:31:22 +00:00
Alexey Shchepin aae0124f61 * src/win32/ejabberd.nsi: Updated (thanks to Sergei Golovan)
SVN Revision: 467
2005-12-13 20:29:00 +00:00
106 changed files with 11376 additions and 4136 deletions
+523 -1
View File
@@ -1,5 +1,527 @@
2006-04-28 Mickael Remond <mickael.remond@process-one.net>
* src/ejabberd.hrl: release 1.1.1
* doc/version.tex: Likewise
* doc/guide.tex: Likewise
2006-04-28 Alexey Shchepin <alexey@sevcom.net>
* src/cyrsasl.erl: Bugfix: anonymous authentication was always
enabled
2006-04-27 Mickael Remond <mickael.remond@process-one.net>
* src/ejabberd_ctl.erl: The status now only returns "started" if
ejabberd is started and ready to accept requests.
2006-04-24 Alexey Shchepin <alexey@sevcom.net>
* doc/guide.tex: Added a documentation for max_stanza_size option
2006-04-24 Mickael Remond <mickael.remond@process-one.net>
* src/ejabberd.hrl: Updated for version 1.1.0.
2006-04-23 Alexey Shchepin <alexey@sevcom.net>
* src/configure: Removed '==' bashism (thanks to Magnus Henoch)
* src/aclocal.m4: Likewise
* src/ejabberd_c2s.erl: Resend messages when leaving negative
presence state (thanks to Magnus Henoch)
2006-04-23 Mickael Remond <mickael.remond@process-one.net>
* src/msgs/fr.msg: Updated
* src/msgs/de.msg: Updated (thanks to Nikolaus Polak)
* src/ejabberd_sm.erl: Now delivering messages to all resources with
max equal priority when this priority is non-negative.
* src/guide.tex: Added documentation for native database configuration.
2006-04-22 Alexey Shchepin <alexey@sevcom.net>
* src/msgs/pt-br.msg: Updated (thanks to Lucius Curado)
* src/xml_stream.erl: Support for stanza size limit (thanks to
Igor Goryachev)
* src/ejabberd_receiver.erl: Likewise
* src/ejabberd_c2s.erl: Likewise
* src/ejabberd_s2s_in.erl: Likewise
* src/ejabberd.cfg.example: Updated
* src/ejabberd_auth.erl: Fixed try_register/3 behaviour
2006-04-20 Mickael Remond <mickael.remond@process-one.net>
* src/ejabberd.cfg.example: Update of the example for anonymous.
Removed unnecessary parameter.
* src/ejabberd_auth_anonymous.erl: Removed unnecessary parameter /
bugfix.
* src/ejabberd_auth.erl: Bugfix: We now are forced to check is an
anonymous user is log under a given user name before trying to
register it.
* doc/guide.tex: Updated (SASL anonymous and anonymous login).
2006-04-19 Alexey Shchepin <alexey@sevcom.net>
* src/msgs/pl.msg: Updated (thanks to Andrzej Smyk)
2006-04-13 Alexey Shchepin <alexey@sevcom.net>
* src/xml_stream.erl: Cleanup
* src/ejabberd_service.erl: Updated to use ejabberd_receiver
2006-04-12 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_c2s.erl: Minor fix
* src/ejabberd_c2s.erl: Bugfix (thanks to Sergei Golovan)
* doc/guide.tex: Updated (thanks to Sergei Golovan)
* src/msgs/nl.msg: Updated (thanks to Sander Devrieze)
2006-04-11 Alexey Shchepin <alexey@sevcom.net>
* src/msgs/es.msg: Updated (thanks to Badlop)
* src/ejabberd.cfg.example: Updated (thanks to Badlop)
* doc/guide.tex: Updated (thanks to Badlop)
2006-04-07 Mickael Remond <mickael.remond@process-one.net>
* src/mod_pubsub/mod_pubsub.erl: Fixed pubsub root node creation
(conflict error was send on node creation)
2006-04-07 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_sm.erl: SASL Anonymous + Anonymous login support
(thanks to Mickael Remond and Magnus Henoch)
* src/ejabberd_c2s.erl: Likewise
* src/ejabberd_auth.erl: Likewise
* src/ejabberd_auth_anonymous.erl: Likewise
* src/cyrsasl.erl: Likewise
* src/cyrsasl_anonymous.erl: Likewise
* src/ejabberd.cfg.example: Likewise
2006-04-06 Alexey Shchepin <alexey@sevcom.net>
* src/expat_erl.c: Use binaries for CDATA
* src/xml.erl: Likewise
2006-04-02 Alexey Shchepin <alexey@sevcom.net>
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
* src/msgs/uk.msg: Likewise
* src/mod_configure.erl: Updated translation strings (thanks to
Sergei Golovan)
* src/web/ejabberd_web_admin.erl: Likewise
* src/ejabberd_s2s_in.erl: Changed a path to PKIX includes (thanks
to Sergei Golovan)
2006-04-01 Mickael Remond <mickael.remond@process-one.net>
* src/Makefile.in: Added an option to compile ejabberd with debug
print-out (make ejabberd_debug=true).
* src/ejabberd_c2s.erl: Added a new debug printout: We can now print
the XML packets send by the server.
2006-03-31 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_c2s.erl: Bugfix
2006-03-25 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_c2s.erl: Enable zlib only over TCP
2006-03-18 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_c2s.erl: Bugfix (thanks to Sergei Golovan)
2006-03-17 Alexey Shchepin <alexey@sevcom.net>
* src/mod_privacy.erl: Fixed privacy list pushing (thanks to
Sergei Golovan)
* src/ejabberd_c2s.erl: Likewise
2006-03-16 Mickael Remond <mickael.remond@process-one.net>
* src/ejabberd_sm.erl: Remove unnecessary use delete_object, which can,
in some context break indexes, when used on non bag table.
2006-03-16 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_c2s.erl: Bugfix
2006-03-14 Alexey Shchepin <alexey@sevcom.net>
* src/xml_stream.erl: Added catching of gen_fsm:send_event errors
* src/ejabberd_s2s_out.erl: Better support for multiple SRV
records (thanks to Sergei Golovan)
* src/mod_muc/mod_muc_log.erl: Support for chatroom logging
(thanks to Badlop)
* src/mod_muc/mod_muc_room.erl: Likewise
* src/mod_muc/Makefile.in: Likewise
* src/mod_muc/Makefile.win32: Likewise
2006-03-11 Alexey Shchepin <alexey@sevcom.net>
* src/gen_iq_handler.erl: Added support for {queues, N} IQ handler
type
2006-03-06 Alexey Shchepin <alexey@sevcom.net>
* src/mod_muc/mod_muc_room.erl: Bugfix
2006-03-05 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_c2s.erl: Bugfix
* src/ejabberd_auth_odbc.erl: Bugfix
2006-03-04 Mickael Remond <mickael.remond@process-one.net>
* src/ejabberd_logger_h.erl: reopen-log function now rename the log
file if it has not been already renamed by a logrotate process. This
change allow ejabberd administrators to rotate log files on Windows
(EJAB-52).
2006-02-27 Alexey Shchepin <alexey@sevcom.net>
* src/web/ejabberd_web_admin.erl: Added a interface for node
updating
* src/ejabberd_update.erl: Added function update_info/0
2006-02-25 Alexey Shchepin <alexey@sevcom.net>
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
* src/msgs/uk.msg: Likewise
* contrib/extract_translations/extract_translations.erl: Minor
update (thanks to Sergei Golovan)
* src/ejabberd_zlib/Makefile.win32: Zlib support for Windows build
(thanks to Sergei Golovan)
* src/Makefile.win32: Likewise
* src/configure.erl: Likewise
2006-02-20 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_ctl.erl: Added API for virtual host specific
commands, removed registered-users command
* src/ejabberd_auth.erl: Added processing of registered-users
command
* src/ejabberd_auth_internal.erl: Likewise
* src/ejabberd_auth_ldap.erl: Likewise
* src/ejabberd_auth_odbc.erl: Likewise
* src/ejabberd_app.erl: Added inititalization of ejabberd_ctl
* src/ejabberd_sm.erl: Minor update
2006-02-18 Alexey Shchepin <alexey@sevcom.net>
* src/mod_irc/mod_irc_connection.erl: Added handling for "%", "&"
and "~" prefixes in IRC nicknames (thanks to Vladimir Kulev)
* src/mod_irc/mod_irc_connection.erl: Added /msg and /ctcp
commands, improved handling of channel topic and kick, mirc colors
now filtered, other minor improvements (thanks to Oleg V. Motienko
and Magnus Henoch)
* src/mod_configure.erl: Improved strings (thanks to Sander
Devrieze)
* src/mod_vcard.erl: Likewise
* src/mod_vcard_ldap.erl: Likewise
* src/mod_vcard_odbc.erl: Likewise
* src/web/ejabberd_web_admin.erl: Likewise
2006-02-15 Alexey Shchepin <alexey@sevcom.net>
* src/mod_service_log.erl: Bugfix (thanks to Badlop)
* src/jd2ejd.erl: Use mod_vcard_odbc if it is loaded (thanks to
Tomasz Sterna)
2006-02-14 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_sm.erl: Added a few ejabberdctl commands
* src/ejabberd_ctl.erl: New interface for command registration
2006-02-13 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_ctl.erl: Partially rewritten
* src/ejabberd_ctl.hrl: Definitions of exit status codes moved
here
2006-02-12 Alexey Shchepin <alexey@sevcom.net>
* src/mod_roster_odbc.erl: Bugfix
2006-02-08 Alexey Shchepin <alexey@sevcom.net>
* src/odbc/ejabberd_odbc.erl: Reconnect on ODBC connection closing
2006-02-07 Mickael Remond <mickael.remond@process-one.net>
* src/ejabberd_auth.erl: plugin authentication modules can now
be used in the configuration file: {auth_method, module} will now
use the module ejabberd_auth_module.erl for authentication.
2006-02-05 Alexey Shchepin <alexey@sevcom.net>
* src/mod_muc/mod_muc_room.erl: Kick non-members when room
reconfigured to be member-only or when membership is revoked in
member-only room, allowed "true" and "false" in x:data forms
(thanks to Sergei Golovan)
* src/mod_configure.erl: Added "Host" parameter to get_form and
set_form functions (thanks to Sergei Golovan)
* src/ejabberd_s2s_out.erl: Bugfix
* src/msgs/es.msg: Updated (thanks to Badlop)
* src/web/ejabberd_web.erl: Bugfix (thanks to Badlop)
* src/mod_irc/mod_irc.erl: Updated copyright dates
* src/mod_muc/mod_muc.erl: Likewise
* src/mod_pubsub/mod_pubsub.erl: Likewise
* src/mod_vcard.erl: Likewise
* src/mod_vcard_ldap.erl: Likewise
* src/mod_vcard_odbc.erl: Likewise
* src/web/ejabberd_web_admin.erl: Likewise
2006-02-03 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_auth_odbc.erl: Added ejabberd_odbc_sup to ejabberd
supervision tree
* src/web/ejabberd_http.erl: Authentication check moved to
ejabberd_web.erl
* src/web/ejabberd_web.erl: Likewise
* src/web/Makefile.in: Added ejabberd_http.hrl dependency
* src/web/ejabberd_http_poll.erl: Updated to use {active, once}
socket mode
* src/mod_irc/mod_irc.erl: Updated to use gen_server behaviour and
ejabberd supervision tree
* src/mod_irc/mod_irc_connection.erl: Likewise
2006-02-02 Mickael Remond <mickael.remond@process-one.net>
* src/configure.ac: --prefix option can now override the default
install dir
* src/configure: Likewise
* src/Makefile.in: Likewise
2006-02-02 Alexey Shchepin <alexey@sevcom.net>
* src/mod_pubsub/mod_pubsub.erl: Updated to use gen_server
behaviour and ejabberd supervision tree
* src/mod_echo.erl: Likewise
2006-02-01 Alexey Shchepin <alexey@sevcom.net>
* src/mod_muc/mod_muc.erl: Added a supervisor for conference room
processes
* src/mod_muc/mod_muc_room.erl: Likewise
2006-01-29 Alexey Shchepin <alexey@sevcom.net>
* src/odbc/pg.sql: Fixed syntax error
2006-01-28 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_router.erl: Updated to use gen_server behaviour
* src/ejabberd_sm.erl: Likewise
* src/ejabberd_s2s.erl: Likewise
* src/gen_iq_handler.erl: Likewise
* src/ejabberd_sup.erl: Added supervisor for ejabberd_receiver
* src/ejabberd_receiver.erl: Updated
2006-01-27 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_update.erl: Support for run-time ejabberd updating
(not completed)
* src/ejabberd_c2s.erl: Added 'update_info' module attribute for
testing ejabberd_update
2006-01-25 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_c2s.erl: Remove top-level xmlns from incoming
stanzas
* src/ejabberd_s2s_in.erl: Likewise
* src/ejabberd_service.erl: Likewise
* src/ejabberd_c2s.erl: Better error handling for stream
compression support (thanks to Sergei Golovan)
2006-01-24 Alexey Shchepin <alexey@sevcom.net>
* src/mod_roster_odbc.erl: Bugfix
2006-01-23 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_sm.erl: Partially rewritten to work more
efficiently and avoid race conditions
* src/ejabberd_c2s.erl: Likewise
2006-01-21 Alexey Shchepin <alexey@sevcom.net>
* src/mod_irc/mod_irc_connection.erl: Cleanup
2006-01-20 Mickael Remond <mickael.remond@process-one.net>
* src/ejabberd_receiver.erl: Added new debugging trace: It is now
possible to dump the XML stream received from a client (usefull for
client debugging).
2006-01-19 Alexey Shchepin <alexey@sevcom.net>
* src/aclocal.m4: Updated for zlib support
* src/configure.ac: Likewise
* src/mod_muc/mod_muc_room.erl: Weakened presence filtering, added
warning in non-anonymous rooms, room destroying updated to latest
JEP-0045, added a number of occupants and room name in room's
disco#info reply, miscellaneous internal changes (thanks to Sergei
Golovan)
* src/mod_muc/mod_muc.erl: Better support for nick unregistration
(thanks to Sergei Golovan)
* src/ejabberd_zlib/ejabberd_zlib.erl: Zlib support (thanks to
Sergei Golovan)
* src/ejabberd_zlib/ejabberd_zlib_drv.c: Likewise
* src/ejabberd_zlib/Makefile.in: Likewise
* src/ejabberd_c2s.erl: Stream compression support (JEP-0138)
* src/ejabberd_receiver.erl: Likewise
* src/mod_disco.erl: Don't split node name before calling hooks
(thanks to Sergei Golovan)
* src/mod_configure.erl: Support for configuration using ad-hoc
commands (thanks to Sergei Golovan)
* src/mod_announce.erl: Support for sending announce messages
using ad-hoc commands (thanks to Sergei Golovan)
* src/mod_adhoc.erl: Ad-hoc support (JEP-0050) (thanks to Magnus
Henoch)
* src/adhoc.erl: Likewise
* src/adhoc.hrl: Likewise
* src/jlib.hrl: Updated (thanks to Sergei Golovan)
* src/gen_mod.erl: Added function is_loaded/2 (thanks to Sergei
Golovan)
* src/ejabberd_service.erl: Changed error message on handshake
error (thanks to Sergei Golovan)
* src/ejabberd.cfg.example: Updated (thanks to Sergei Golovan)
2006-01-13 Mickael Remond <mickael.remond@process-one.net>
* src/odbc/ejabberd_odbc.erl: underscore and percent are now only
escaped in like queries. MySQL where not escaping those escaped
characters in other context.
* src/mod_vcard_odbc.erl: likewise.
* src/odbc/mysql.sql: Fixed MySQL database creation script: Was
not properly working with all MySQL version.
* src/odbc/ejabberd_odbc.erl: Added a way to retry database
connection connect for 5 minutes when the connection is lost. No
further connection is retry after.
* src/odbc/ejabberd_odbc_sup.erl: likewise.
2006-01-13 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_service.erl: Bugfix
* src/ejabberd_receiver.erl: Rewritten to use {active, once} mode
for socket
* src/ejabberd_c2s.erl: Update
* src/ejabberd_listener.erl: Likewise
* src/ejabberd_s2s_in.erl: Likewise
* src/ejabberd_s2s_out.erl: Likewise
* src/ejabberd_service.erl: Likewise
* src/shaper.erl: Likewise
* src/tls/tls.erl: Likewise
* src/web/ejabberd_http.erl: Likewise
2006-01-02 Mickael Remond <mickael.remond@process-one.net>
* src/odbc/ejabberd_odbc.erl: Native MySQL support
2005-12-24 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_logger_h.erl: Speed optimizations
2005-12-22 Alexey Shchepin <alexey@sevcom.net>
* src/Makefile.in: Clean results of ASN.1 compiler (thanks to
Sergei Golovan)
* src/win32/ejabberd.nsh: Removed (thanks to Sergei Golovan)
2005-12-22 Mickael Remond <mickael.remond@process-one.net>
* src/odbc/ejabberd_odbc.erl: Added error message on ODBC
connection to help ODBC configuration troubleshooting.
2005-12-22 Alexey Shchepin <alexey@sevcom.net>
* src/Makefile.win32: Updated (thanks to Sergei Golovan)
* src/configure.erl: Likewise
* src/win32/CheckReqs.ini: Likewise
* src/win32/CheckReqs1.ini: Likewise
* src/win32/CheckReqs1H.ini: Likewise
* src/win32/ejabberd.nsh: Likewise
* src/win32/ejabberd.nsi: Likewise
* src/odbc/ejabberd_odbc.erl: Bugfix
2005-12-21 Mickael Remond <mickael.remond@process-one.net>
* src/odbc/mysql.sql: Database description for MySQL Database 4.1
(Max version, with Innodb)
* src/ejabberd_auth_odbc.erl: MySQL ODBC support
* src/mod_offline_odbc.erl: Likewise
* src/mod_vcard_odbc.erl: Likewsie
* src/mod_roster_odbc.erl: Likewise
* src/odbc/ejabberd_odbc.erl: Likewise
* src/mod_last_odbc.erl: Likewise
2005-12-16 Mickael Remond <mickael.remond@process-one.net>
* doc/Makefile: Added helper to generate the docs (Thanks to Sander
Devrieze)
* doc/guide.tex: Added ejabberd 1.0.0 release notes.
* doc/dev.tex: Code clean-up (Thanks to Sander Devrieze)
2005-12-16 Alexey Shchepin <alexey@sevcom.net>
* doc/features.tex: Added feature sheet (Thanks to Sander
Devrieze)
2005-12-14 Alexey Shchepin <alexey@sevcom.net>
* src/mod_irc/iconv_erl.c: Bugfix (thanks to Tomas Janousek)
2005-12-13 Alexey Shchepin <alexey@sevcom.net>
* src/win32/ejabberd.nsi: Updated (thanks to Maxim Ryazanov)
* src/ejabberd.hrl: Updated version
* src/ejabberd.app: Likewise
* doc/version.tex: Likewise
@@ -1750,7 +2272,7 @@
2003-11-27 Alexey Shchepin <alexey@sevcom.net>
* examples/mtr/ejabberd.cfg: Updatted (thanks to Marshall T. Rose)
* examples/mtr/ejabberd.cfg: Updated (thanks to Marshall T. Rose)
* src/ejabberd_auth.erl: LDAP attribute that holds user ID now
configurable
-3
View File
@@ -1,7 +1,5 @@
Win32 build: Make it possible to compile with +debug_info flag.
mod_muc logging
admin interface
users management
statistics about each user
@@ -10,7 +8,6 @@ admin interface
S2S:
check "id" attributes in db:verify packets
more correctly work with SRV DNS records (priority, weight, etc...)
make roster set to work in one transaction
add traffic shapers to c2s connection before authentification
more traffic shapers
@@ -108,7 +108,7 @@ parse_form(Dir, File, Form, Used) ->
ok
end.
process_string(Dir, File, Str, Used) ->
process_string(_Dir, File, Str, Used) ->
case {ets:lookup(translations, Str), Used} of
{[{_Key, _Trans}], unused} ->
ets:delete(translations, Str);
+41 -3
View File
@@ -1,11 +1,49 @@
# $Id$
all: guide.html dev.html
all: release pdf html
release:
@echo "Notes for the releaser:"
@echo "* Do not forget to add a link to the release notes in guide.tex"
@echo "* Do not forget to change the version number in version.tex"
@echo "* Do not forget to update the features in introduction.tex (including \new{} and \improved{} tags)."
@read -p "press any key to continue"
html: guide.html dev.html features.html
pdf: guide.pdf features.pdf
clean:
rm -f *.aux
rm -f *.haux
rm -f *.html
rm -f *.htoc
rm -f *.idx
rm -f *.ilg
rm -f *.ind
rm -f *.log
rm -f *.out
rm -f *.pdf
rm -f *.toc
guide.html: guide.tex
hevea -charset ISO8859-1 guide.tex
hevea guide.tex
hevea guide.tex
dev.html: dev.tex
hevea -charset ISO8859-1 dev.tex
hevea dev.tex
hevea dev.tex
features.html: features.tex
hevea features.tex
hevea features.tex
guide.pdf: guide.tex
pdflatex guide.tex
pdflatex guide.tex
pdflatex guide.tex
makeindex guide.idx
pdflatex guide.tex
features.pdf: features.tex
pdflatex features.tex
+203 -125
View File
@@ -1,99 +1,186 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD><TITLE>Ejabberd Developers Guide</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=ISO8859-1">
<META name="GENERATOR" content="hevea 1.07">
<HEAD>
<TITLE>Ejabberd 1.0.0 Developers Guide</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<META name="GENERATOR" content="hevea 1.08">
<STYLE type="text/css">
.toc{list-style:none;}
.title{margin:auto;text-align:center}
.center{text-align:center;margin-left:auto;margin-right:auto;}
.flushleft{text-align:left;margin-left:0ex;margin-right:auto;}
.flushright{text-align:right;margin-left:auto;margin-right:0ex;}
DIV TABLE{margin-left:inherit;margin-right:inherit;}
PRE{text-align:left;margin-left:0ex;margin-right:auto;}
BLOCKQUOTE{margin-left:4ex;margin-right:4ex;text-align:left;}
.part{margin:auto;text-align:center}
</STYLE>
</HEAD>
<BODY >
<!--HEVEA command line is: /usr/bin/hevea -charset ISO8859-1 dev.tex -->
<!--HEVEA command line is: hevea dev.tex -->
<!--HTMLHEAD-->
<!--ENDHTML-->
<!--PREFIX <ARG ></ARG>-->
<!--CUT DEF section 1 -->
<BR>
<BR>
<A NAME="sec:titlepage"></A>
<H1 ALIGN=center>Ejabberd Developers Guide</H1>
<H3 ALIGN=center>Alexey Shchepin<BR>
<TABLE CLASS="title">
<TR><TD>
<H1 CLASS="titlemain">Ejabberd 1.0.0 Developers Guide</H1>
<H3 CLASS="titlerest">Alexey Shchepin<BR>
<A HREF="mailto:alexey@sevcom.net"><TT>mailto:alexey@sevcom.net</TT></A><BR>
<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT></A></H3>
<H3 ALIGN=center>August 21, 2005</H3><DIV ALIGN=center>
<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT></A></H3></TD>
</TR></TABLE><BR>
<BR>
<DIV CLASS="center">
<IMG SRC="logo.png">
</DIV><BR>
<BR>
<BR>
</DIV>
<BLOCKQUOTE CLASS="quotation"><I>I can thoroughly recommend ejabberd for ease of setup &ndash;
Kevin Smith, Current maintainer of the Psi project</I></BLOCKQUOTE>
<!--TOC section Contents-->
<!--TOC section Table of Contents-->
<H2 CLASS="section">Contents</H2><!--SEC END -->
<H2>Table of Contents</H2><!--SEC END -->
<UL><LI>
<UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc1">1&nbsp;&nbsp;Introduction</A>
<UL><LI>
<A HREF="#htoc2">1.1&nbsp;&nbsp;How it works</A>
<UL><LI>
<A HREF="#htoc3">1.1.1&nbsp;&nbsp;Router</A>
<LI><A HREF="#htoc4">1.1.2&nbsp;&nbsp;Local Router</A>
<LI><A HREF="#htoc5">1.1.3&nbsp;&nbsp;Session Manager</A>
<LI><A HREF="#htoc6">1.1.4&nbsp;&nbsp;S2S Manager</A>
<UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc2">1.1&nbsp;&nbsp;Key Features</A>
<LI CLASS="li-toc"><A HREF="#htoc3">1.2&nbsp;&nbsp;Additional Features</A>
</UL>
<LI CLASS="li-toc"><A HREF="#htoc4">2&nbsp;&nbsp;How it works</A>
<UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc5">2.1&nbsp;&nbsp;Router</A>
<LI CLASS="li-toc"><A HREF="#htoc6">2.2&nbsp;&nbsp;Local Router</A>
<LI CLASS="li-toc"><A HREF="#htoc7">2.3&nbsp;&nbsp;Session Manager</A>
<LI CLASS="li-toc"><A HREF="#htoc8">2.4&nbsp;&nbsp;S2S Manager</A>
</UL>
<LI><A HREF="#htoc7">2&nbsp;&nbsp;XML representation</A>
<LI><A HREF="#htoc8">3&nbsp;&nbsp;Module <TT>xml</TT></A>
<LI><A HREF="#htoc9">4&nbsp;&nbsp;Module <TT>xml_stream</TT></A>
<LI><A HREF="#htoc10">5&nbsp;&nbsp;<TT>ejabberd</TT> modules</A>
<UL><LI>
<A HREF="#htoc11">5.1&nbsp;&nbsp;<CODE>gen_mod</CODE> behaviour</A>
<LI><A HREF="#htoc12">5.2&nbsp;&nbsp;Module <CODE>gen_iq_handler</CODE></A>
<LI><A HREF="#htoc13">5.3&nbsp;&nbsp;Services</A>
<LI CLASS="li-toc"><A HREF="#htoc9">3&nbsp;&nbsp;XML representation</A>
<LI CLASS="li-toc"><A HREF="#htoc10">4&nbsp;&nbsp;Module <TT>xml</TT></A>
<LI CLASS="li-toc"><A HREF="#htoc11">5&nbsp;&nbsp;Module <TT>xml_stream</TT></A>
<LI CLASS="li-toc"><A HREF="#htoc12">6&nbsp;&nbsp;<TT>ejabberd</TT> modules</A>
<UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc13">6.1&nbsp;&nbsp;gen_mod behaviour</A>
<LI CLASS="li-toc"><A HREF="#htoc14">6.2&nbsp;&nbsp;Module gen_iq_handler</A>
<LI CLASS="li-toc"><A HREF="#htoc15">6.3&nbsp;&nbsp;Services</A>
</UL>
</UL>
<!--TOC section Introduction-->
<H2><A NAME="htoc1">1</A>&nbsp;&nbsp;Introduction</H2><!--SEC END -->
<H2 CLASS="section"><A NAME="htoc1">1</A>&nbsp;&nbsp;Introduction</H2><!--SEC END -->
<A NAME="sec:intro"></A>
<TT>ejabberd</TT> is a Free and Open Source fault-tolerant distributed Jabber
server. It is written mostly in Erlang.<BR>
<A NAME="sec:intr"></A>
<TT>ejabberd</TT> is a free (GPL) distributed fault-tolerant Jabber/XMPP server and is mainly written in <A HREF="http://www.erlang.org/">Erlang</A>.<BR>
<BR>
The main features of <TT>ejabberd</TT> are:
<UL><LI>
Works on most of popular platforms: *nix (tested on Linux, FreeBSD and
NetBSD) and Win32
<LI>Distributed: You can run <TT>ejabberd</TT> on a cluster of machines to let all of
them serve one Jabber domain.
<LI>Fault-tolerance: You can setup an <TT>ejabberd</TT> cluster so that all the
information required for a properly working service will be stored
permanently on more than one node. This means that if one of the nodes
crashes, then the others will continue working without disruption.
You can also add or replace nodes ``on the fly''.
<LI>Support for virtual hosting
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0045.html">Multi-User Chat</A> service
<LI>Built-in IRC transport
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A> service
<LI>Built-in Jabber Users Directory service based on users vCards
<LI>Built-in web-based administration interface
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> service
<LI>SSL support
<LI>Support for LDAP authentication
<LI>Ability to interface with external components (JIT, MSN-t, Yahoo-t, etc.)
<LI>Migration from jabberd14 is possible
<LI>Mostly XMPP-compliant
<LI>Support for <A HREF="http://www.jabber.org/jeps/jep-0030.html">Service Discovery</A>.
<LI>Support for <A HREF="http://www.jabber.org/jeps/jep-0039.html">Statistics Gathering</A>.
<LI>Support for <TT>xml:lang</TT>
</UL>
<!--TOC subsection How it works-->
<TT>ejabberd</TT> is designed to be a stable, standards compliant, and feature rich Jabber/XMPP server.<BR>
<BR>
<TT>ejabberd</TT> is suitable for small servers, whether they need to be scalable or not, as well as extremely big servers.<BR>
<BR>
<!--TOC subsection Key Features-->
<H3><A NAME="htoc2">1.1</A>&nbsp;&nbsp;How it works</H3><!--SEC END -->
<H3 CLASS="subsection"><A NAME="htoc2">1.1</A>&nbsp;&nbsp;Key Features</H3><!--SEC END -->
<A NAME="sec:keyfeatures"></A>
<TT>ejabberd</TT> is:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Multiplatform: <TT>ejabberd</TT> runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.<BR>
<BR>
<LI CLASS="li-itemize">Distributed: You can run <TT>ejabberd</TT> on a cluster of machines and all of them will serve one Jabber domain. When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.<BR>
<BR>
<LI CLASS="li-itemize">Fault-tolerant: You can deploy an <TT>ejabberd</TT> cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced &#8220;on the fly&#8221;.<BR>
<BR>
<LI CLASS="li-itemize">Administrator Friendly: <TT>ejabberd</TT> is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Comprehensive documentation.
<LI CLASS="li-itemize">Straightforward installers for Windows and Linux.
<LI CLASS="li-itemize">Web interface for administration tasks.
<LI CLASS="li-itemize">Shared Roster groups.
<LI CLASS="li-itemize">Command line administration tool.
<LI CLASS="li-itemize">Can integrate with existing authentication mechanisms.
<LI CLASS="li-itemize">Capability to send announce messages.
</UL><BR>
<BR>
<LI CLASS="li-itemize">Internationalized: <TT>ejabberd</TT> leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Translated in 11 languages.
<LI CLASS="li-itemize">Support for <A HREF="http://www.ietf.org/rfc/rfc3490.txt">IDNA</A>.
</UL><BR>
<BR>
<LI CLASS="li-itemize">Open Standards: <TT>ejabberd</TT> is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
<UL CLASS="itemize"><LI CLASS="li-itemize">
Fully XMPP compliant
<LI CLASS="li-itemize">XML-based protocol
<LI CLASS="li-itemize"><A HREF="http://ejabberd.jabber.ru/protocols">Many JEPs supported</A>.
</UL></UL>
<!--TOC subsection Additional Features-->
<H3 CLASS="subsection"><A NAME="htoc3">1.2</A>&nbsp;&nbsp;Additional Features</H3><!--SEC END -->
<A NAME="sec:addfeatures"></A>
Besides common Jabber server features, <TT>ejabberd</TT> comes with a wide range of other features:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Modular: <TT>ejabberd</TT>'s modular architecture allows easy customization:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Load only the modules you want.
<LI CLASS="li-itemize">Extend <TT>ejabberd</TT> with your own custom modules.
</UL>
<LI CLASS="li-itemize">Security
<UL CLASS="itemize"><LI CLASS="li-itemize">
SASL and STARTTLS for c2s and s2s connections.
<LI CLASS="li-itemize">STARTTLS and Dialback s2s connections.
<LI CLASS="li-itemize">Obsolete SSL for c2s connections also supported.
<LI CLASS="li-itemize">Web interface accessible via HTTPS secure access.
</UL>
<LI CLASS="li-itemize">Databases
<UL CLASS="itemize"><LI CLASS="li-itemize">
Native PostgreSQL support.
<LI CLASS="li-itemize">Mnesia.
<LI CLASS="li-itemize">ODBC data storage support.
</UL>
<LI CLASS="li-itemize">Authentication
<UL CLASS="itemize"><LI CLASS="li-itemize">
LDAP.
<LI CLASS="li-itemize">External Authentication script.
<LI CLASS="li-itemize">Internal Authentication.
</UL>
<LI CLASS="li-itemize">The ability to interface via external components with networks such as:
<UL CLASS="itemize"><LI CLASS="li-itemize">
AIM
<LI CLASS="li-itemize">ICQ
<LI CLASS="li-itemize">MSN
</UL>
<LI CLASS="li-itemize">Others
<UL CLASS="itemize"><LI CLASS="li-itemize">
IPv6 support both for c2s and s2s connections.
<LI CLASS="li-itemize">Support for virtual hosting.
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> service
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0045.html">Multi-User Chat</A> module.
<LI CLASS="li-itemize">IRC transport.
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A> component.
<LI CLASS="li-itemize">Users Directory based on users vCards.
<LI CLASS="li-itemize">Statistics via Statistics Gathering (<A HREF="http://www.jabber.org/jeps/jep-0039.html">JEP-0039</A>).
</UL>
</UL>
<!--TOC section How it works-->
<H2 CLASS="section"><A NAME="htoc4">2</A>&nbsp;&nbsp;How it works</H2><!--SEC END -->
<A NAME="sec:howitworks"></A>
A Jabber domain is served by one or more <TT>ejabberd</TT> nodes. These nodes can
@@ -105,15 +192,15 @@ needed because all nodes exchange information about connected users, S2S
connections, registered services, etc...<BR>
<BR>
Each <TT>ejabberd</TT> node have following modules:
<UL><LI>
<UL CLASS="itemize"><LI CLASS="li-itemize">
router;
<LI>local router.
<LI>session manager;
<LI>S2S manager;
<LI CLASS="li-itemize">local router.
<LI CLASS="li-itemize">session manager;
<LI CLASS="li-itemize">S2S manager;
</UL>
<!--TOC subsubsection Router-->
<!--TOC subsection Router-->
<H4><A NAME="htoc3">1.1.1</A>&nbsp;&nbsp;Router</H4><!--SEC END -->
<H3 CLASS="subsection"><A NAME="htoc5">2.1</A>&nbsp;&nbsp;Router</H3><!--SEC END -->
This module is the main router of Jabber packets on each node. It routes
them based on their destinations domains. It has two tables: local and global
@@ -123,26 +210,26 @@ searches in global table, and is routed to the appropriate <TT>ejabberd</TT> nod
process. If it does not exists in either tables, then it sent to the S2S
manager.<BR>
<BR>
<!--TOC subsubsection Local Router-->
<!--TOC subsection Local Router-->
<H4><A NAME="htoc4">1.1.2</A>&nbsp;&nbsp;Local Router</H4><!--SEC END -->
<H3 CLASS="subsection"><A NAME="htoc6">2.2</A>&nbsp;&nbsp;Local Router</H3><!--SEC END -->
This module routes packets which have a destination domain equal to this server
name. If destination JID has a non-empty user part, then it routed to the
session manager, else it is processed depending on it's content.<BR>
<BR>
<!--TOC subsubsection Session Manager-->
<!--TOC subsection Session Manager-->
<H4><A NAME="htoc5">1.1.3</A>&nbsp;&nbsp;Session Manager</H4><!--SEC END -->
<H3 CLASS="subsection"><A NAME="htoc7">2.3</A>&nbsp;&nbsp;Session Manager</H3><!--SEC END -->
This module routes packets to local users. It searches for what user resource
packet must be sended via presence table. If this resource is connected to
this node, it is routed to C2S process, if it connected via another node, then
the packet is sent to session manager on that node.<BR>
<BR>
<!--TOC subsubsection S2S Manager-->
<!--TOC subsection S2S Manager-->
<H4><A NAME="htoc6">1.1.4</A>&nbsp;&nbsp;S2S Manager</H4><!--SEC END -->
<H3 CLASS="subsection"><A NAME="htoc8">2.4</A>&nbsp;&nbsp;S2S Manager</H3><!--SEC END -->
This module routes packets to other Jabber servers. First, it checks if an
open S2S connection from the domain of the packet source to the domain of
@@ -153,11 +240,11 @@ does not exist, then it is opened and registered.<BR>
<BR>
<!--TOC section XML representation-->
<H2><A NAME="htoc7">2</A>&nbsp;&nbsp;XML representation</H2><!--SEC END -->
<H2 CLASS="section"><A NAME="htoc9">3</A>&nbsp;&nbsp;XML representation</H2><!--SEC END -->
<A NAME="sec:xmlrepr"></A>
Each XML stanza is represented as the following tuple:
<PRE>
<PRE CLASS="verbatim">
XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
Name = string()
Attrs = [Attr]
@@ -167,12 +254,12 @@ XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
ElementOrCDATA = XMLElement | CDATA
CDATA = {xmlcdata, string()}
</PRE>E.&nbsp;g. this stanza:
<PRE>
<PRE CLASS="verbatim">
&lt;message to='test@conference.example.org' type='groupchat'&gt;
&lt;body&gt;test&lt;/body&gt;
&lt;/message&gt;
</PRE>is represented as the following structure:
<PRE>
<PRE CLASS="verbatim">
{xmlelement, "message",
[{"to", "test@conference.example.org"},
{"type", "groupchat"}],
@@ -182,29 +269,28 @@ XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
</PRE>
<!--TOC section Module <TT>xml</TT>-->
<H2><A NAME="htoc8">3</A>&nbsp;&nbsp;Module <TT>xml</TT></H2><!--SEC END -->
<H2 CLASS="section"><A NAME="htoc10">4</A>&nbsp;&nbsp;Module <TT>xml</TT></H2><!--SEC END -->
<A NAME="sec:xmlmod"></A>
<DL COMPACT=compact><DT>
<CODE><B>element_to_string(El) -&gt; string()</B></CODE><DD>
<PRE>
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>element_to_string(El) -&gt; string()</CODE>
<PRE CLASS="verbatim">
El = XMLElement
</PRE>Returns string representation of XML stanza <TT>El</TT>.<BR>
<BR>
<DT><CODE><B>crypt(S) -&gt; string()</B></CODE><DD>
<PRE>
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>crypt(S) -&gt; string()</CODE>
<PRE CLASS="verbatim">
S = string()
</PRE>Returns string which correspond to <TT>S</TT> with encoded XML special
characters.<BR>
<BR>
<DT><CODE><B>remove_cdata(ECList) -&gt; EList</B></CODE><DD>
<PRE>
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>remove_cdata(ECList) -&gt; EList</CODE>
<PRE CLASS="verbatim">
ECList = [ElementOrCDATA]
EList = [XMLElement]
</PRE><TT>EList</TT> is a list of all non-CDATA elements of ECList.<BR>
<BR>
<DT><CODE><B>get_path_s(El, Path) -&gt; Res</B></CODE><DD>
<PRE>
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>get_path_s(El, Path) -&gt; Res</CODE>
<PRE CLASS="verbatim">
El = XMLElement
Path = [PathItem]
PathItem = PathElem | PathAttr | PathCDATA
@@ -215,17 +301,16 @@ Name = string()
Res = string() | XMLElement
</PRE>If <TT>Path</TT> is empty, then returns <TT>El</TT>. Else sequentially
consider elements of <TT>Path</TT>. Each element is one of:
<DL COMPACT=compact><DT>
<CODE><B>{elem, Name}</B></CODE><DD> <TT>Name</TT> is name of subelement of
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>{elem, Name}</CODE> <TT>Name</TT> is name of subelement of
<TT>El</TT>, if such element exists, then this element considered in
following steps, else returns empty string.
<DT><CODE><B>{attr, Name}</B></CODE><DD> If <TT>El</TT> have attribute <TT>Name</TT>, then
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>{attr, Name}</CODE> If <TT>El</TT> have attribute <TT>Name</TT>, then
returns value of this attribute, else returns empty string.
<DT><CODE><B>cdata</B></CODE><DD> Returns CDATA of <TT>El</TT>.
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>cdata</CODE> Returns CDATA of <TT>El</TT>.
</DL><BR>
<BR>
<DT><B>TODO:</B><DD>
<PRE>
<DT CLASS="dt-description"><DD CLASS="dd-description">TODO:
<PRE CLASS="verbatim">
get_cdata/1, get_tag_cdata/1
get_attr/2, get_attr_s/2
get_tag_attr/2, get_tag_attr_s/2
@@ -233,12 +318,11 @@ Res = string() | XMLElement
</PRE></DL>
<!--TOC section Module <TT>xml_stream</TT>-->
<H2><A NAME="htoc9">4</A>&nbsp;&nbsp;Module <TT>xml_stream</TT></H2><!--SEC END -->
<H2 CLASS="section"><A NAME="htoc11">5</A>&nbsp;&nbsp;Module <TT>xml_stream</TT></H2><!--SEC END -->
<A NAME="sec:xmlstreammod"></A>
<DL COMPACT=compact><DT>
<CODE><B>parse_element(Str) -&gt; XMLElement | {error, Err}</B></CODE><DD>
<PRE>
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>parse_element(Str) -&gt; XMLElement | {error, Err}</CODE>
<PRE CLASS="verbatim">
Str = string()
Err = term()
</PRE>Parses <TT>Str</TT> using XML parser, returns either parsed element or error
@@ -246,28 +330,27 @@ Err = term()
</DL>
<!--TOC section <TT>ejabberd</TT> modules-->
<H2><A NAME="htoc10">5</A>&nbsp;&nbsp;<TT>ejabberd</TT> modules</H2><!--SEC END -->
<H2 CLASS="section"><A NAME="htoc12">6</A>&nbsp;&nbsp;<TT>ejabberd</TT> modules</H2><!--SEC END -->
<A NAME="sec:emods"></A>
<!--TOC subsection <CODE>gen_mod</CODE> behaviour-->
<!--TOC subsection gen_mod behaviour-->
<H3><A NAME="htoc11">5.1</A>&nbsp;&nbsp;<CODE>gen_mod</CODE> behaviour</H3><!--SEC END -->
<H3 CLASS="subsection"><A NAME="htoc13">6.1</A>&nbsp;&nbsp;gen_mod behaviour</H3><!--SEC END -->
<A NAME="sec:genmod"></A>
TBD<BR>
<BR>
<!--TOC subsection Module <CODE>gen_iq_handler</CODE>-->
<!--TOC subsection Module gen_iq_handler-->
<H3><A NAME="htoc12">5.2</A>&nbsp;&nbsp;Module <CODE>gen_iq_handler</CODE></H3><!--SEC END -->
<H3 CLASS="subsection"><A NAME="htoc14">6.2</A>&nbsp;&nbsp;Module gen_iq_handler</H3><!--SEC END -->
<A NAME="sec:geniqhandl"></A>
The module <CODE>gen_iq_handler</CODE> allows to easily write handlers for IQ packets
of particular XML namespaces that addressed to server or to users bare JIDs.<BR>
<BR>
In this module the following functions are defined:
<DL COMPACT=compact><DT>
<CODE><B>add_iq_handler(Component, Host, NS, Module, Function, Type)</B></CODE><DD>
<PRE>
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>add_iq_handler(Component, Host, NS, Module, Function, Type)</CODE>
<PRE CLASS="verbatim">
Component = Module = Function = atom()
Host = NS = string()
Type = no_queue | one_queue | parallel
@@ -275,24 +358,22 @@ Type = no_queue | one_queue | parallel
virtual host <CODE>Host</CODE> that contain child of namespace <CODE>NS</CODE> in
<CODE>Component</CODE>. Queueing discipline is <CODE>Type</CODE>. There are at least
two components defined:
<DL COMPACT=compact><DT>
<CODE><B>ejabberd_local</B></CODE><DD> Handles packets that addressed to server JID;
<DT><CODE><B>ejabberd_sm</B></CODE><DD> Handles packets that addressed to users bare JIDs.
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>ejabberd_local</CODE> Handles packets that addressed to server JID;
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>ejabberd_sm</CODE> Handles packets that addressed to users bare JIDs.
</DL>
<DT><CODE><B>remove_iq_handler(Component, Host, NS)</B></CODE><DD>
<PRE>
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>remove_iq_handler(Component, Host, NS)</CODE>
<PRE CLASS="verbatim">
Component = atom()
Host = NS = string()
</PRE>Removes IQ handler on virtual host <CODE>Host</CODE> for namespace <CODE>NS</CODE> from
<CODE>Component</CODE>.
</DL>
Handler function must have the following type:
<DL COMPACT=compact><DT>
<CODE><B>Module:Function(From, To, IQ)</B></CODE><DD>
<PRE>
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>Module:Function(From, To, IQ)</CODE>
<PRE CLASS="verbatim">
From = To = jid()
</PRE></DL>
<PRE>
<PRE CLASS="verbatim">
-module(mod_cputime).
-behaviour(gen_mod).
@@ -330,13 +411,13 @@ process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -&gt;
</PRE>
<!--TOC subsection Services-->
<H3><A NAME="htoc13">5.3</A>&nbsp;&nbsp;Services</H3><!--SEC END -->
<H3 CLASS="subsection"><A NAME="htoc15">6.3</A>&nbsp;&nbsp;Services</H3><!--SEC END -->
<A NAME="sec:services"></A>
TBD<BR>
<BR>
TODO: use <CODE>proc_lib</CODE>
<PRE>
<PRE CLASS="verbatim">
-module(mod_echo).
-behaviour(gen_mod).
@@ -375,9 +456,6 @@ stop(Host) -&gt;
<!--HTMLFOOT-->
<!--ENDHTML-->
<!--FOOTER-->
<HR SIZE=2>
<BLOCKQUOTE><EM>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
</EM><A HREF="http://pauillac.inria.fr/~maranget/hevea/index.html"><EM>H<FONT SIZE=2><sup>E</sup></FONT>V<FONT SIZE=2><sup>E</sup></FONT>A</EM></A><EM>.
</EM></BLOCKQUOTE>
</BODY>
<HR SIZE=2><BLOCKQUOTE CLASS="quote"><EM>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
</EM><A HREF="http://pauillac.inria.fr/~maranget/hevea/index.html"><EM>H<FONT SIZE=2><sup>E</sup></FONT>V<FONT SIZE=2><sup>E</sup></FONT>A</EM></A><EM>.</EM></BLOCKQUOTE></BODY>
</HTML>
+78 -81
View File
@@ -1,14 +1,20 @@
\documentclass[10pt]{article}
\documentclass[a4paper,10pt]{article}
%% Packages
\usepackage{graphics}
\usepackage{hevea}
\usepackage{makeidx}
\usepackage{verbatim}
%% Index
\makeindex
% Remove the index anchors from the HTML version to save size and bandwith.
\newcommand{\ind}[1]{\begin{latexonly}\index{#1}\end{latexonly}}
%% Images
\newcommand{\logoscale}{0.7}
\newcommand{\imgscale}{0.58}
\newcommand{\insimg}[1]{\insscaleimg{\imgscale}{#1}}
\newcommand{\insscaleimg}[2]{
\imgsrc{#2}{}
\begin{latexonly}
@@ -16,83 +22,74 @@
\end{latexonly}
}
%% Various
\newcommand{\ns}[1]{\texttt{#1}}
\newcommand{\ejabberd}{\texttt{ejabberd}}
\newcommand{\Jabber}{Jabber}
\newcommand{\modregister}{\texttt{mod\_register}}
\newcommand{\modroster}{\texttt{mod\_roster}}
\newcommand{\modconfigure}{\texttt{mod\_configure}}
\newcommand{\moddisco}{\texttt{mod\_disco}}
\newcommand{\modstats}{\texttt{mod\_stats}}
\newcommand{\modvcard}{\texttt{mod\_vcard}}
\newcommand{\modoffline}{\texttt{mod\_offline}}
\newcommand{\modecho}{\texttt{mod\_echo}}
\newcommand{\modprivate}{\texttt{mod\_private}}
\newcommand{\modtime}{\texttt{mod\_time}}
\newcommand{\modversion}{\texttt{mod\_version}}
%% Modules
\newcommand{\module}[1]{\texttt{#1}}
\newcommand{\modannounce}{\module{mod\_announce}}
\newcommand{\modconfigure}{\module{mod\_configure}}
\newcommand{\moddisco}{\module{mod\_disco}}
\newcommand{\modirc}{\module{mod\_irc}}
\newcommand{\modlast}{\module{mod\_last}}
\newcommand{\modmuc}{\module{mod\_muc}}
\newcommand{\modecho}{\module{mod\_echo}}
\newcommand{\modoffline}{\module{mod\_offline}}
\newcommand{\modprivacy}{\module{mod\_privacy}}
\newcommand{\modprivate}{\module{mod\_private}}
\newcommand{\modpubsub}{\module{mod\_pubsub}}
\newcommand{\modregister}{\module{mod\_register}}
\newcommand{\modroster}{\module{mod\_roster}}
\newcommand{\modservicelog}{\module{mod\_service\_log}}
\newcommand{\modsharedroster}{\module{mod\_shared\_roster}}
\newcommand{\modstats}{\module{mod\_stats}}
\newcommand{\modtime}{\module{mod\_time}}
\newcommand{\modvcard}{\module{mod\_vcard}}
\newcommand{\modversion}{\module{mod\_version}}
\newcommand{\tjepref}[2]{\footahref{http://www.jabber.org/jeps/jep-#1.html}{#2}}
\newcommand{\jepref}[1]{\tjepref{#1}{JEP-#1}}
%\setcounter{tocdepth}{3}
\title{Ejabberd Developers Guide}
%% Title page
\include{version}
\title{Ejabberd \version\ Developers Guide}
\author{Alexey Shchepin \\
\ahrefurl{mailto:alexey@sevcom.net} \\
\ahrefurl{xmpp:aleksey@jabber.ru}}
\date{August 21, 2005}
%% Options
\newcommand{\marking}[1]{#1} % Marking disabled
\newcommand{\quoting}[2][yozhik]{} % Quotes disabled
\newcommand{\new}{\begin{latexonly}\marginpar{\textsc{new}}\end{latexonly}} % Highlight new features
\newcommand{\improved}{\begin{latexonly}\marginpar{\textsc{improved}}\end{latexonly}} % Highlight improved features
\newcommand{\moreinfo}[1]{} % Hide details
%% Footnotes
\newcommand{\tjepref}[2]{\footahref{http://www.jabber.org/jeps/jep-#1.html}{#2}}
\newcommand{\jepref}[1]{\tjepref{#1}{JEP-#1}}
\begin{document}
\label{sec:titlepage}
\begin{titlepage}
\maketitle{}
{\centering
\insscaleimg{\logoscale}{logo.png}
\begin{center}
{\insscaleimg{\logoscale}{logo.png}
\par
}
\end{center}
\begin{quotation}\textit{I can thoroughly recommend ejabberd for ease of setup --
Kevin Smith, Current maintainer of the Psi project}\end{quotation}
\end{titlepage}
%\newpage
\tableofcontents{}
\newpage
\section{Introduction}
\label{sec:intro}
% Input introduction.tex
\input{introduction}
\ejabberd{} is a Free and Open Source fault-tolerant distributed \Jabber{}
server. It is written mostly in Erlang.
The main features of \ejabberd{} are:
\begin{itemize}
\item Works on most of popular platforms: *nix (tested on Linux, FreeBSD and
NetBSD) and Win32
\item Distributed: You can run \ejabberd{} on a cluster of machines to let all of
them serve one Jabber domain.
\item Fault-tolerance: You can setup an \ejabberd{} cluster so that all the
information required for a properly working service will be stored
permanently on more than one node. This means that if one of the nodes
crashes, then the others will continue working without disruption.
You can also add or replace nodes ``on the fly''.
\item Support for virtual hosting
\item Built-in \tjepref{0045}{Multi-User Chat} service
\item Built-in IRC transport
\item Built-in \tjepref{0060}{Publish-Subscribe} service
\item Built-in Jabber Users Directory service based on users vCards
\item Built-in web-based administration interface
\item Built-in \tjepref{0025}{HTTP Polling} service
\item SSL support
\item Support for LDAP authentication
\item Ability to interface with external components (JIT, MSN-t, Yahoo-t, etc.)
\item Migration from jabberd14 is possible
\item Mostly XMPP-compliant
\item Support for \tjepref{0030}{Service Discovery}.
\item Support for \tjepref{0039}{Statistics Gathering}.
\item Support for \ns{xml:lang}
\end{itemize}
\subsection{How it works}
\section{How it works}
\label{sec:howitworks}
@@ -115,7 +112,7 @@ Each \ejabberd{} node have following modules:
\end{itemize}
\subsubsection{Router}
\subsection{Router}
This module is the main router of \Jabber{} packets on each node. It routes
them based on their destinations domains. It has two tables: local and global
@@ -126,14 +123,14 @@ process. If it does not exists in either tables, then it sent to the S2S
manager.
\subsubsection{Local Router}
\subsection{Local Router}
This module routes packets which have a destination domain equal to this server
name. If destination JID has a non-empty user part, then it routed to the
session manager, else it is processed depending on it's content.
\subsubsection{Session Manager}
\subsection{Session Manager}
This module routes packets to local users. It searches for what user resource
packet must be sended via presence table. If this resource is connected to
@@ -141,7 +138,7 @@ this node, it is routed to C2S process, if it connected via another node, then
the packet is sent to session manager on that node.
\subsubsection{S2S Manager}
\subsection{S2S Manager}
This module routes packets to other \Jabber{} servers. First, it checks if an
open S2S connection from the domain of the packet source to the domain of
@@ -189,20 +186,20 @@ is represented as the following structure:
\label{sec:xmlmod}
\begin{description}
\item[\verb|element_to_string(El) -> string()|]
\item{\verb|element_to_string(El) -> string()|}
\begin{verbatim}
El = XMLElement
\end{verbatim}
Returns string representation of XML stanza \texttt{El}.
\item[\verb|crypt(S) -> string()|]
\item{\verb|crypt(S) -> string()|}
\begin{verbatim}
S = string()
\end{verbatim}
Returns string which correspond to \texttt{S} with encoded XML special
characters.
\item[\verb|remove_cdata(ECList) -> EList|]
\item{\verb|remove_cdata(ECList) -> EList|}
\begin{verbatim}
ECList = [ElementOrCDATA]
EList = [XMLElement]
@@ -211,7 +208,7 @@ EList = [XMLElement]
\item[\verb|get_path_s(El, Path) -> Res|]
\item{\verb|get_path_s(El, Path) -> Res|}
\begin{verbatim}
El = XMLElement
Path = [PathItem]
@@ -225,15 +222,15 @@ Res = string() | XMLElement
If \texttt{Path} is empty, then returns \texttt{El}. Else sequentially
consider elements of \texttt{Path}. Each element is one of:
\begin{description}
\item[\verb|{elem, Name}|] \texttt{Name} is name of subelement of
\item{\verb|{elem, Name}|} \texttt{Name} is name of subelement of
\texttt{El}, if such element exists, then this element considered in
following steps, else returns empty string.
\item[\verb|{attr, Name}|] If \texttt{El} have attribute \texttt{Name}, then
\item{\verb|{attr, Name}|} If \texttt{El} have attribute \texttt{Name}, then
returns value of this attribute, else returns empty string.
\item[\verb|cdata|] Returns CDATA of \texttt{El}.
\item{\verb|cdata|} Returns CDATA of \texttt{El}.
\end{description}
\item[TODO:]
\item{TODO:}
\begin{verbatim}
get_cdata/1, get_tag_cdata/1
get_attr/2, get_attr_s/2
@@ -247,7 +244,7 @@ Res = string() | XMLElement
\label{sec:xmlstreammod}
\begin{description}
\item[\verb!parse_element(Str) -> XMLElement | {error, Err}!]
\item{\verb!parse_element(Str) -> XMLElement | {error, Err}!}
\begin{verbatim}
Str = string()
Err = term()
@@ -261,12 +258,12 @@ Err = term()
\label{sec:emods}
\subsection{\verb|gen_mod| behaviour}
\subsection{gen\_mod behaviour}
\label{sec:genmod}
TBD
\subsection{Module \verb|gen_iq_handler|}
\subsection{Module gen\_iq\_handler}
\label{sec:geniqhandl}
The module \verb|gen_iq_handler| allows to easily write handlers for IQ packets
@@ -274,7 +271,7 @@ of particular XML namespaces that addressed to server or to users bare JIDs.
In this module the following functions are defined:
\begin{description}
\item[\verb|add_iq_handler(Component, Host, NS, Module, Function, Type)|]
\item{\verb|add_iq_handler(Component, Host, NS, Module, Function, Type)|}
\begin{verbatim}
Component = Module = Function = atom()
Host = NS = string()
@@ -285,10 +282,10 @@ Type = no_queue | one_queue | parallel
\verb|Component|. Queueing discipline is \verb|Type|. There are at least
two components defined:
\begin{description}
\item[\verb|ejabberd_local|] Handles packets that addressed to server JID;
\item[\verb|ejabberd_sm|] Handles packets that addressed to users bare JIDs.
\item{\verb|ejabberd_local|} Handles packets that addressed to server JID;
\item{\verb|ejabberd_sm|} Handles packets that addressed to users bare JIDs.
\end{description}
\item[\verb|remove_iq_handler(Component, Host, NS)|]
\item{\verb|remove_iq_handler(Component, Host, NS)|}
\begin{verbatim}
Component = atom()
Host = NS = string()
@@ -299,7 +296,7 @@ Host = NS = string()
Handler function must have the following type:
\begin{description}
\item[\verb|Module:Function(From, To, IQ)|]
\item{\verb|Module:Function(From, To, IQ)|}
\begin{verbatim}
From = To = jid()
\end{verbatim}
+134
View File
@@ -0,0 +1,134 @@
\documentclass[a4paper,10pt]{article}
%% Packages
\usepackage{epsfig}
\usepackage{fancyhdr}
\usepackage{graphics}
\usepackage{hevea}
\usepackage[pdftex,colorlinks,unicode,urlcolor=blue,linkcolor=blue,pdftitle=Ejabberd\
Feature\ Sheet,pdfauthor=Sander\
Devrieze,pdfsubject=ejabberd,pdfkeywords=ejabberd]{hyperref}
\usepackage{verbatim}
\usepackage{xcolor}
%% Index
% Remove the index anchors from the HTML version to save size and bandwith.
\newcommand{\ind}[1]{\begin{latexonly}\index{#1}\end{latexonly}}
%% Images
\newcommand{\logoscale}{0.7}
\newcommand{\imgscale}{0.58}
\newcommand{\insimg}[1]{\insscaleimg{\imgscale}{#1}}
\newcommand{\insscaleimg}[2]{
\imgsrc{#2}{}
\begin{latexonly}
\scalebox{#1}{\includegraphics{#2}}
\end{latexonly}
}
%% Various
\newcommand{\bracehack}{\def\{{\char"7B}\def\}{\char"7D}}
\newcommand{\titem}[1]{\item[\bracehack\texttt{#1}]}
\newcommand{\ns}[1]{\texttt{#1}}
\newcommand{\jid}[1]{\texttt{#1}}
\newcommand{\option}[1]{\texttt{#1}}
\newcommand{\poption}[1]{{\bracehack\texttt{#1}}}
\newcommand{\node}[1]{\texttt{#1}}
\newcommand{\term}[1]{\texttt{#1}}
\newcommand{\shell}[1]{\texttt{#1}}
\newcommand{\ejabberd}{\texttt{ejabberd}}
\newcommand{\Jabber}{Jabber}
%% Title page
\include{version}
\title{Ejabberd \version\ Feature Sheet}
\author{Sander Devrieze \\
\ahrefurl{mailto:s.devrieze@pandora.be} \\
\ahrefurl{xmpp:sander@devrieze.dyndns.org}}
% Options
\newcommand{\marking}[1]{\textbf{\begin{large}\textcolor{ejblue}{#1}\end{large}}} % Marking enabled
\newcommand{\quoting}[2][yozhik]{\begin{quotation}\textcolor{#1}{\textit{#2}}\end{quotation}} % Quotes enabled
\newcommand{\new}{\begin{latexonly}\marginpar{\textcolor{red}{\textsc{new}}}\end{latexonly}} % Highlight new features
\newcommand{\improved}{\begin{latexonly}\marginpar{\textcolor{orange}{\textsc{improved}}}\end{latexonly}} % Highlight improved features
\newcommand{\moreinfo}[1]{} % Hide details
\setcounter{secnumdepth}{-1} % Disable section numbering
%% Footnotes
\begin{latexonly}
\global\parskip=9pt plus 3pt minus 1pt
\global\parindent=0pt
\gdef\ahrefurl#1{\href{#1}{\texttt{#1}}}
\gdef\footahref#1#2{#2\footnote{\href{#1}{\texttt{#1}}}}
\end{latexonly}
\newcommand{\tjepref}[2]{\footahref{http://www.jabber.org/jeps/jep-#1.html}{#2}}
\newcommand{\jepref}[1]{\tjepref{#1}{JEP-#1}}
%% Fancy header
\fancyhf{}
\pagestyle{fancy}
\rhead{\textcolor{ejblue}{The expandable Jabber daemon.}}
\renewcommand{\headrule}{{\color{ejblue}%
\hrule width\headwidth height\headrulewidth \vskip-\headrulewidth}}
\lhead{\setlength{\unitlength}{-6mm}
\begin{picture}(0,0)
\put(5.8,3.25){\includegraphics[width=1.3\textwidth]{yozhikheader.png}}
\end{picture}}
% Official ejabberd colours
\definecolor{ejblue} {cmyk}{1.000,0.831,0.000,0.537} %RGB: 0,0,118 HTML: 000076
\definecolor{ejgreenyellow}{cmyk}{0.079,0.000,0.275,0.102} %RGB: 209,229,159 HTML: d1e59f
\definecolor{ejgreendark} {cmyk}{0.131,0.000,0.146,0.220} %RGB: 166,199,162 HTML: a6c7a2
\definecolor{ejgreen} {cmyk}{0.077,0.000,0.081,0.078} %RGB: 216,236,215 HTML: d8ecd7
\definecolor{ejgreenwhite} {cmyk}{0.044,0.000,0.048,0.020} %RGB: 239,250,238 HTML: effaee
\definecolor{yozhik} {cmyk}{0.000,0.837,1.000,0.424} %RGB: 147,0,0 HTML: 930000
\begin{document}
\label{sec:titlepage}
\begin{titlepage}
\maketitle{}
\thispagestyle{empty}
\begin{center}
{\insscaleimg{\logoscale}{logo.png}
\par
}
\end{center}
\quoting{I can thoroughly recommend ejabberd for ease of setup --
Kevin Smith, Current maintainer of the Psi project}
\end{titlepage}
\newpage
% Set the page counter to 2 so that the titlepage and the second page do not
% have the same page number. This fixes the PDFLaTeX warning "destination with
% the same identifier".
\begin{latexonly}
\setcounter{page}{2}
\end{latexonly}
\pagecolor{ejgreenwhite}
% Input introduction.tex
\input{introduction}
\end{document}
%% TODO
% * illustrations (e.g. screenshot from web interface)
% * commented parts
% * slides, guide and html version
% * cleaning and improving LaTeX code
% * key features: something like this (shorter)? (more focussed on Erlang now): "To reach the goal of high
% availability, performance and clustering, ejabberd is written in Erlang, a programming language perfectly
% suited for this. Besides that, some parts are written in C to also incude the advantages of this language. In
% short, ejabberd is a perfect mix of mainly Erlang code, peppered with some C code to get the final touch!"
% <picture of a cocktail>
% * key features: saying that ejabberd the only XMPP server is that can do real clustering:
% http://www.jivesoftware.org/forums/thread.jspa?threadID=14602
% "What I find interesting is that *no* XMPP servers truly provide clustering. This includes all the commercial
% servers. The one partial exception appears to be ejabberd, which can cluster certain data such as sessions,
% but not all services such as MUC."
% * try it today: links to migration tutorials
+1508 -675
View File
File diff suppressed because it is too large Load Diff
+383 -66
View File
@@ -44,24 +44,31 @@
%% Modules
\newcommand{\module}[1]{\texttt{#1}}
\newcommand{\modadhoc}{\module{mod\_adhoc}}
\newcommand{\modannounce}{\module{mod\_announce}}
\newcommand{\modconfigure}{\module{mod\_configure}}
\newcommand{\moddisco}{\module{mod\_disco}}
\newcommand{\modecho}{\module{mod\_echo}}
\newcommand{\modirc}{\module{mod\_irc}}
\newcommand{\modlast}{\module{mod\_last}}
\newcommand{\modlastodbc}{\module{mod\_last\_odbc}}
\newcommand{\modmuc}{\module{mod\_muc}}
\newcommand{\modecho}{\module{mod\_echo}}
\newcommand{\modmuclog}{\module{mod\_muc\_log}}
\newcommand{\modoffline}{\module{mod\_offline}}
\newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}}
\newcommand{\modprivacy}{\module{mod\_privacy}}
\newcommand{\modprivate}{\module{mod\_private}}
\newcommand{\modpubsub}{\module{mod\_pubsub}}
\newcommand{\modregister}{\module{mod\_register}}
\newcommand{\modroster}{\module{mod\_roster}}
\newcommand{\modrosterodbc}{\module{mod\_roster\_odbc}}
\newcommand{\modservicelog}{\module{mod\_service\_log}}
\newcommand{\modsharedroster}{\module{mod\_shared\_roster}}
\newcommand{\modstats}{\module{mod\_stats}}
\newcommand{\modtime}{\module{mod\_time}}
\newcommand{\modvcard}{\module{mod\_vcard}}
\newcommand{\modvcardldap}{\module{mod\_vcard\_ldap}}
\newcommand{\modvcardodbc}{\module{mod\_vcard\_odbc}}
\newcommand{\modversion}{\module{mod\_version}}
%% Common options
@@ -144,8 +151,10 @@ To compile \ejabberd{} on a ``Unix-like'' operating system, you need:
\item GNU Make;
\item GCC;
\item libexpat 1.95 or higher;
\item Erlang/OTP R8B or higher;
\item Erlang/OTP R9C-2 or higher;
\item OpenSSL 0.9.6 or higher (optional).
\item Zlib 1.2.3 or higher (optional).
\item GNU Iconv 1.8 or higher (optional, not needed at all on systems with GNU libc).
\end{itemize}
\subsubsection{Windows}
@@ -155,13 +164,14 @@ To compile \ejabberd{} on a ``Unix-like'' operating system, you need:
To compile \ejabberd{} on a Windows flavour, you need:
\begin{itemize}
\item MS Visual C++ 6.0 Compiler
\item \footahref{http://erlang.org/download.html}{Erlang/OTP R8B or higher}
\item \footahref{http://erlang.org/download.html}{Erlang/OTP R9C-2 or higher}
\item \footahref{http://sourceforge.net/project/showfiles.php?group\_id=10127\&package\_id=11277}{Expat 1.95.7 or higher}
\item
\footahref{http://www.gnu.org/software/libiconv/}{Iconv 1.9.1}
\footahref{http://www.gnu.org/software/libiconv/}{GNU Iconv 1.9.1}
(optional)
\item \footahref{http://www.slproweb.com/products/Win32OpenSSL.html}{Shining Light OpenSSL}
(to enable SSL connections)
\item \footahref{http://www.zlib.net/}{Zlib 1.2.3 or higher}
\end{itemize}
\subsection{Obtaining \ejabberd{}}
@@ -337,7 +347,7 @@ Examples:
\end{itemize}
\item Serving two domains:
\begin{verbatim}
{hosts, ["one.org", "two.org"]}.
{hosts, ["example.net", "example.com"]}.
\end{verbatim}
\end{itemize}
@@ -512,15 +522,17 @@ tuple with the following elements:
Currently next modules are implemented:
\begin{table}[H]
\centering
\begin{tabular}{|l|l|l|l|l|}
\def\arraystretch{1.4}
\begin{tabular}{|l|l|p{87mm}|}
\hline \texttt{ejabberd\_c2s}& Description& Handles c2s connections.\\
\cline{2-3} & Options& \texttt{access}, \texttt{certfile}, \texttt{inet6},
\texttt{ip}, \texttt{shaper}, \texttt{ssl}, \texttt{tls},
\texttt{starttls},\\
& &\texttt{starttls\_required}\\
\texttt{ip}, \texttt{max\_stanza\_size}, \texttt{shaper}, \texttt{ssl},
\texttt{tls}, \texttt{starttls}, \texttt{starttls\_required},
\texttt{zlib}\\
\hline \texttt{ejabberd\_s2s\_in}& Description& Handles incoming s2s
connections.\\
\cline{2-3} & Options& \texttt{inet6}, \texttt{ip}\\
\cline{2-3} & Options& \texttt{inet6}, \texttt{ip},
\texttt{max\_stanza\_size}\\
\hline \texttt{ejabberd\_service}& Description& Interacts with external
components (*).\\
\cline{2-3} & Options& \texttt{access}, \texttt{hosts}, \texttt{inet6},
@@ -558,6 +570,10 @@ The following options are available:
\titem{inet6} \ind{options!inet6}\ind{IPv6}Set up the socket for IPv6.
\titem{\{ip, IPAddress\}} \ind{options!ip}This option specifies which network
interface to listen for. For example \verb|{ip, {192, 168, 1, 1}}|.
\titem{\{max\_stanza\_size, Size\}} \ind{options!max\_stanza\_size}This
option specifies an approximate maximal size in bytes of XML stanzas.
For example \verb|{max\_stanza\_size, 65536}|. The default value
is ``\term{infinity}''.
\titem{\{shaper, <access rule>\}} \ind{options!shaper}This option defines a
shaper for the port (see section~\ref{sec:configshaper}). The default value
is ``\term{none}''.
@@ -575,6 +591,11 @@ The following options are available:
\titem{tls} \ind{options!tls}\ind{TLS}This option specifies that traffic on
the port will be encrypted using SSL immediately after connecting. You
should also set the \option{certfile} option.
\titem{zlib} \ind{options!zlib}\ind{protocols!JEP-0138: Stream Compression}\ind{Zlib}This
option specifies that Zlib stream compression (as defined in \jepref{0138})
is available on connections to the port. Client cannot use stream compression and
stream encryption simultaneously, so if you specify both \option{tls} (or \option{ssl})
and \option{zlib} the latter option will not affect connection at all.
\titem{web\_admin} \ind{options!web\_admin}\ind{web interface}This option
enables the web interface for \ejabberd{} administration which is available
at \verb|http://server:port/admin/|. Login and password are the username and
@@ -694,33 +715,30 @@ services you have to make the transports log and do \ind{XDB}XDB by themselves:
\ind{modules}
The option \term{modules} defines the list of modules that will be loaded after
\ejabberd{} startup. Each entry in the list is a tuple in which the first
\ejabberd{}'s startup. Each entry in the list is a tuple in which the first
element is the name of a module and the second is a list of options for that
module. Read section~\ref{sec:modules} for detailed information about each
module.
module. Read section~\ref{sec:modules} for detailed information about modules.
Example:\ind{modules!overview}
\begin{verbatim}
Examples:
\begin{itemize}
\item In this simple example, only the module \modecho{} is loaded and no
options are specified between square brackets:
\begin{verbatim}
{modules,
[{mod_register, []},
{mod_roster, []},
{mod_privacy, []},
{mod_configure, []},
{mod_disco, []},
{mod_stats, []},
{mod_vcard, []},
{mod_offline, []},
{mod_announce, [{access, announce}]},
{mod_echo, [{hosts, ["echo.example.org"]}]},
{mod_private, []},
{mod_irc, []},
{mod_muc, []},
{mod_pubsub, []},
{mod_time, [{iqdisc, no_queue}]},
{mod_last, []},
[{mod_echo, []}
]}.
\end{verbatim}
\item The second example is also simple: the modules \modecho{}, \modtime{}, and
\modversion{} are loaded without options. Remark that, besides the last entry,
all entries end with a comma:
\begin{verbatim}
{modules,
[{mod_echo, []},
{mod_time, []},
{mod_version, []}
]}.
\end{verbatim}
\end{itemize}
\subsubsection{Virtual Hosting}
\label{sec:configvirtualhost}
@@ -735,27 +753,27 @@ syntax:
Examples:
\begin{itemize}
\item Domain \jid{one.org} is using the internal authentication method while
domain \jid{two.org} is using the \ind{LDAP}LDAP server running on the domain
\item Domain \jid{example.net} is using the internal authentication method while
domain \jid{example.com} is using the \ind{LDAP}LDAP server running on the domain
\jid{localhost} to perform authentication:
\begin{verbatim}
{host_config, "one.org", [{auth_method, internal}]}.
{host_config, "example.net", [{auth_method, internal}]}.
{host_config, "two.org", [{auth_method, ldap},
{host_config, "example.com", [{auth_method, ldap},
{ldap_servers, ["localhost"]},
{ldap_uidattr, "uid"},
{ldap_rootdn, "dc=localdomain"},
{ldap_rootdn, "dc=example,dc=com"},
{ldap_password, ""}]}.
\end{verbatim}
\item Domain \jid{one.org} is using \ind{ODBC}ODBC to perform authentication
while domain \jid{two.org} is using the LDAP servers running on the domains
\item Domain \jid{example.net} is using \ind{ODBC}ODBC to perform authentication
while domain \jid{example.com} is using the LDAP servers running on the domains
\jid{localhost} and \jid{otherhost}:
\begin{verbatim}
{host_config, "one.org", [{auth_method, odbc},
{host_config, "example.net", [{auth_method, odbc},
{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}]}.
{host_config, "two.org", [{auth_method, ldap},
{host_config, "example.com", [{auth_method, ldap},
{ldap_servers, ["localhost", "otherhost"]},
{ldap_uidattr, "uid"},
{ldap_rootdn, "dc=localdomain"},
@@ -764,6 +782,147 @@ Examples:
\end{verbatim}
\end{itemize}
\subsubsection{SASL anonymous and anonymous login}
\label{sec:configsaslanonymous}
\ind{sasl anonymous}\ind{anonymous login}
The configuration of the anonymous mode can be done with three
\term{host\_config} parameters:
\begin{itemize}
\item \term{auth\_method}: This value is used for defining the authentication method:
internal, odbc, ldap, external). You now have a special extra option to enable
anonymous mode: anonymous.
\item \term{allow\_multiple\_connections}: This option can be either true or false and
is only used when the anonymous mode is enabled. Setting it to true means that
the same username will be able to be taken several time in anonymous login
mode if different resource are used to connect. This option is only useful in
very special cases. It defaults to false.
\item \term{anonymous\_protocol}: This option can take three values:
\term{sasl\_anon}, \term{login\_anon} or both. \term{sasl\_anon} means that
SASL anonymous mode is enabled. \term{login\_anon} means that anonymous
login mode is enabled. both means that SASL anonymous and login anonymous are
enabled.
\end{itemize}
Those options are defined for each virtual host with the \term{host\_config}
parameter (see section~\ref{sec:configvirtualhost}).
Examples:
\begin{itemize}
\item To enable anonymous login on a virtual host:
\begin{verbatim}
{host_config, "public.example.org", [{auth_method, anonymous},
{anonymous_protocol, login_anon}]}.
\end{verbatim}
\item To enable anonymous login and internal authentication on a virtual host:
\begin{verbatim}
{host_config, "public.example.org", [{auth_method, [anonymous,internal]},
{anonymous_protocol, login_anon}]}.
\end{verbatim}
\item To enable SASL anonymous on a virtual host:
\begin{verbatim}
{host_config, "public.example.org", [{auth_method, [anonymous]},
{anonymous_protocol, sasl_anon}]}.
\end{verbatim}
\item To enable SASL anonymous and anonymous login on a virtual host:
\begin{verbatim}
{host_config, "public.example.org", [{auth_method, [anonymous]},
{anonymous_protocol, both}]}.
\end{verbatim}
\item To enable SASL anonymous, anonymous login and internal authentication on
a virtual host:
\begin{verbatim}
{host_config, "public.example.org", [{auth_method, [anonymous,internal]},
{anonymous_protocol, both}]}.
\end{verbatim}
\end{itemize}
A detailled tutorial on SASL anonymous and anonymous login support is
available from:
\ahrefurl{http://support.process-one.net/doc/display/MESSENGER/Anonymous+users+support}
\subsection{Relational Database Support}
\label{sec:relationaldatabase}
\subsubsection{Authentication against a relational database}
\label{sec:relationaldatabaseauth}
ejabberd use its internal Mnesia database as a default. It is however possible
to use relational database to store persistant, long-living data. When
switching the \term{auth\_method} from internal to \term{odbc}, you are
telling ejabberd to use a relational database for authentication. Different
\term{auth\_method} values can be used for different virtual hosts in the
system.
The option value name be misleading, as the method name is use both for access
to relational database through ODBC or through the native interface. In any
case, the first step is to define the odbc \term{auth\_method}. For example:
\begin{verbatim}
{host_config, "public.example.org", [{auth_method, [odbc]}]}.
\end{verbatim}
The actual database access is defined in the option \term{odbc\_server}. Its
value is use to defined if we want to use ODBC, or one of the two native
interface available, PostgreSQL or MySQL.
To use a relational database through ODBC, you can pass the ODBC connection
string as \term{odbc\_server} parameter. For example:
\begin{verbatim}
{odbc_server, "DSN=database;UID=ejabberd;PWD=password"}.
\end{verbatim}
To use the native PostgreSQL interface, you can pass a tuple of the following form as
parameter:
\begin{verbatim}
{pgsql, "Server", "Database", "Username", "Password"}
\end{verbatim}
\term{pgsql} is a keyword that should be kept as is. For example:
\begin{verbatim}
{odbc_server, {pgsql, "localhost", "database", "ejabberd", "password"}}.
\end{verbatim}
Note that you need to install the Erlang PgSQL library first. This library is
available from Sourceforge: \ahrefurl{http://cvs.sourceforge.net/viewcvs.py/jungerl/jungerl/lib/pgsql/}
To use the native MySQL interface, the same tuple should be passed, except
that you now have to use the \term{mysql} keyword instead of the \term{pgsql}
keyword:
\begin{verbatim}
{mysql, "Server", "Database", "Username", "Password"}
\end{verbatim}
\term{mysql} is a keyword that should be kept as is. For example:
\begin{verbatim}
{odbc_server, {mysql, "localhost", "test", "root", "password"}}.
\end{verbatim}
Note that you need to install the Erlang MySQL library first. This library is
directly available from Process-one website: \ahrefurl{http://support.process-one.net/doc/display/CONTRIBS/Yxa}
A detailled tutorial to set-up \ejabberd{} using the native MySQL interface is
available from: \ahrefurl{http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver}
\subsubsection{Relational database for other modules}
\label{sec:relationaldatabaseextra}
It is possible to use a relational database to store pieces of
information. You can do this by changing the module name to a name with an
\term{\_odbc} suffix in \ejabberd{} config file. You can use a relational
database for the following data:
\begin{itemize}
\item Last connection date and time: Use \term{mod\_last\_odbc} instead of
\term{mod\_last}.
\item Offline messages: Use \term{mod\_offline\_odbc} instead of
\term{mod\_offline}.
\item Rosters: Use \term{mod\_roster\_odbc} instead of \term{mod\_roster}.
\item Users' VCARD: Use \term{mod\_vcard\_odbc} instead of \term{mod\_vcard}.
\end{itemize}
\subsection{Creating an Initial Administrator}
\label{sec:initialadmin}
@@ -828,18 +987,18 @@ Examples:
\ind{protocols!JEP-0025: HTTP Polling}HTTP Polling interface. In this example
you should point your web browser to \verb|http://example.org:5280/admin/| to
administer all virtual hosts or to
\verb|http://example.org:5280/admin/server/two.org/| to administer only the
virtual host \jid{two.org}. Before you get access to the web interface you
need to enter as username, the JID and password from a registered user that is
allowed to configure \ejabberd{}. In this example you can enter as username
``\jid{admin@one.org}'' to administer all virtual hosts (first URL). If you
log in with ``\jid{admin@two.org}'' on \\
\verb|http://example.org:5280/admin/server/two.org/| you can only administer
the virtual host \jid{two.org}.
\verb|http://example.org:5280/admin/server/example.com/| to administer only
the virtual host \jid{example.com}. Before you get access to the web interface
you need to enter as username, the JID and password from a registered user
that is allowed to configure \ejabberd{}. In this example you can enter as
username ``\jid{admin@example.net}'' to administer all virtual hosts (first
URL). If you log in with ``\jid{admin@example.com}'' on \\
\verb|http://example.org:5280/admin/server/example.com/| you can only
administer the virtual host \jid{example.com}.
\begin{verbatim}
...
{acl, admins, {user, "admin", "one.org"}}.
{host_config, "two.org", [{acl, admins, {user, "admin", "two.org"}}]}.
{acl, admins, {user, "admin", "example.net"}}.
{host_config, "example.com", [{acl, admins, {user, "admin", "example.com"}}]}.
{access, configure, [{allow, admins}]}.
...
{hosts, ["example.org"]}.
@@ -923,7 +1082,7 @@ Additional information:
\label{sec:firewall}
\ind{firewall}\ind{ports}\ind{SASL}\ind{TLS}\ind{clustering!ports}
You need to take the following ports in mind when configuring your firewall:
You need to take the following TCP ports in mind when configuring your firewall:
\begin{table}[H]
\centering
\begin{tabular}{|l|l|}
@@ -1098,11 +1257,77 @@ domain.
%\ind{backup}
\appendix{}
\section{Built-in Modules}
\label{sec:modules}
\ind{modules}
\subsection{Overview}
\label{sec:modoverview}
\ind{modules!overview}\ind{XMPP compliancy}
The following table lists all modules available in the official \ejabberd{}
distribution. You can find more
\footahref{http://ejabberd.jabber.ru/contributions}{contributed modules} on the
ejabberd website. Please remember that these contributions might not work or
that they can contain severe bugs and security leaks. Therefore, use them at
your own risk!
You can see which database backend each module needs by looking at the suffix:
\begin{itemize}
\item ``\_ldap'', this means that the module needs an LDAP server as backend.
\item ``\_odbc'', this means that the module needs an ODBC compatible database,
a MySQL database, or a PostgreSQL database as backend.
\item Nothing, this means that the modules uses Erlang's built-in database
Mnesia as backend.
\end{itemize}
\begin{table}[H]
\centering
\begin{tabular}{|l|l|l|l|}
\hline Module & Feature & Dependencies & Needed for XMPP? \\
\hline \hline \modadhoc{} & Ad-Hoc Commands (\jepref{0050}) & & No \\
\hline \modannounce{} & Manage announcements & \modadhoc{} & No \\
\hline \modconfigure{} & Support for online & \modadhoc{} & No \\
& configuration of ejabberd & & \\
\hline \moddisco{} & Service Discovery (\jepref{0030}) & & No \\
\hline \modecho{} & Echoes Jabber packets & & No \\
\hline \modirc{} & IRC transport & & No \\
\hline \modlast{} & Last Activity (\jepref{0012}) & & No \\
\hline \modlastodbc{} & Last Activity (\jepref{0012}) & ODBC compatible & No \\
& & database, MySQL & \\
& & or PostgreSQL & \\
\hline \modmuc{} & Multi-User Chat (\jepref{0045}) & & No \\
\hline \modmuclog{} & Multi-User Chat room logging & \modmuc{} & No \\
\hline \modoffline{} & Offline message storage & & No \\
\hline \modofflineodbc{} & Offline message storage & ODBC compatible & No \\
& & database, MySQL & \\
& & or PostgreSQL & \\
\hline \modprivacy{} & Blocking Communication & & Yes \\
\hline \modprivate{} & Private XML Storage (\jepref{0049}) & & No \\
\hline \modpubsub{} & Publish-Subscribe (\jepref{0060}) & & No \\
\hline \modregister{} & In-Band Registration (\jepref{0077}) & & No \\
\hline \modroster{} & Roster management & & Yes (*) \\
\hline \modrosterodbc{} & Roster management & ODBC compatible & Yes (*) \\
& & database, MySQL & \\
& & or PostgreSQL & \\
\hline \modservicelog{} & Copy user messages to logger service & & No \\
\hline \modsharedroster{} & Shared roster management & \modroster{} or & No \\
& & \modrosterodbc{} & \\
\hline \modstats{} & Statistics Gathering (\jepref{0039}) & & No \\
\hline \modtime{} & Entity Time (\jepref{0090}) & & No \\
\hline \modvcard{} & vcard-temp (\jepref{0054}) & & No \\
\hline \modvcardldap{} & vcard-temp (\jepref{0054}) & LDAP server & No \\
\hline \modvcardodbc{} & vcard-temp (\jepref{0054}) & ODBC compatible & No \\
& & database, MySQL & \\
& & or PostgreSQL & \\
\hline \modversion{} & Software Version (\jepref{0092}) & & No\\
\hline
\end{tabular}
\end{table}
(*) This module or a similar one with another database backend is needed for
XMPP compliancy.
\subsection{Common Options}
\label{sec:modcommonopts}
@@ -1174,12 +1399,12 @@ Examples:
]}.
\end{verbatim}
\end{itemize}
\item Serving the echo module on tho domains:
\item Serving the echo module on two domains:
\begin{verbatim}
{modules,
[
...
{mod_echo, [{hosts, ["echo.one.org", "echo.two.org"]}]},
{mod_echo, [{hosts, ["echo.example.net", "echo.example.com"]}]},
...
]}.
\end{verbatim}
@@ -1383,13 +1608,14 @@ Examples:
...
]}.
\end{verbatim}
% bug in current svn!!: irc-transport.two.org will *not* show up in the service discovery items; instead you will see irc.two.org!!!!
\item In next example the IRC transport is available on two virtual hosts
with different prefixes on each host. Moreover, the transport is only
accessible by paying customers registered on our domains and on other servers.
% bug in current svn!!: irc-transport.example.com will *not* show up in the service discovery items; instead you will see irc.example.com!!!!
\item In next example the IRC transport is available on the two virtual hosts
\jid{example.net} and \jid{example.com} with different prefixes on each host.
Moreover, the transport is only accessible by paying customers registered on
our domains and on other servers.
\begin{verbatim}
{acl, paying_customers, {user, "customer1", "one.org"}}.
{acl, paying_customers, {user, "customer2", "two.org"}}.
{acl, paying_customers, {user, "customer1", "example.net"}}.
{acl, paying_customers, {user, "customer2", "example.com"}}.
{acl, paying_customers, {user, "customer3", "example.org"}}.
...
{access, paying_customers, [{allow, paying_customers},
@@ -1399,7 +1625,7 @@ Examples:
[
...
{mod_irc, [{access, paying_customers},
{hosts, ["irc.one.org", "irc-transport.two.org"]}]},
{hosts, ["irc.example.net", "irc-transport.example.com"]}]},
...
]}.
\end{verbatim}
@@ -1448,7 +1674,9 @@ Options:
\titem{access\_admin} \ind{options!access\_admin}This option specifies
who is allowed to administrate the Multi-User Chat service (the default
value is \term{none}, which means that only the room creator can
administer his room).
administer his room). By sending a message to the service JID,
administrators can send service messages that will be displayed in every
active room.
\end{description}
Examples:
@@ -1456,7 +1684,11 @@ Examples:
\item In the first example everyone is allowed to use the Multi-User Chat
service. Everyone will also be able to create new rooms but only the user
\jid{admin@example.org} is allowed to administrate any room. In this
example he is also a global administrator.
example he is also a global administrator. When \jid{admin@example.org}
sends a message such as ``Tomorrow, the \Jabber{} server will be moved
to new hardware. This will involve service breakdowns around 23:00 UMT.
We apologise for this inconvenience.'' to \jid{conference.example.org},
it will be displayed in all active rooms.
\begin{verbatim}
{acl, admins, {user, "admin", "example.org"}}.
...
@@ -1474,10 +1706,14 @@ Examples:
\item In the second example the Multi-User Chat service is only accessible by
paying customers registered on our domains and on other servers. Of course
the administrator is also allowed to access rooms. In addition, he is the
only authority able to create and administer rooms.
only authority able to create and administer rooms. When
\jid{admin@example.org} sends a message such as ``Tomorrow, the \Jabber{}
server will be moved to new hardware. This will involve service breakdowns
around 23:00 UMT. We apologise for this inconvenience.'' to
\jid{conference.example.org}, it will be displayed in all active rooms.
\begin{verbatim}
{acl, paying_customers, {user, "customer1", "one.org"}}.
{acl, paying_customers, {user, "customer2", "two.org"}}.
{acl, paying_customers, {user, "customer1", "example.net"}}.
{acl, paying_customers, {user, "customer2", "example.com"}}.
{acl, paying_customers, {user, "customer3", "example.org"}}.
{acl, admins, {user, "admin", "example.org"}}.
...
@@ -1498,6 +1734,79 @@ Examples:
\end{verbatim}
\end{itemize}
\subsection{\modmuclog{}}
\label{sec:modmuclog}
\ind{modules!\modmuclog{}}
This module enables optional logging of Multi-User Chat conversations to HTML.
Once you enable this module, join a chatroom with enought privileges using a MUC capable Jabber client,
request the configuration form and there you will have an option to enable chatroom logging.
Some of the features of generated logs:
\begin{itemize}
\item A lot of information about chatroom is added on top of the page: Room title, JID, subject author, subject and configuration.
\item Room title and JID are links to join the chatroom (using XMPP-IRI).
\item Subject and chatroom configuration changes are tracked and displayed.
\item Joins, leaves, nick changes, kicks, bans and /me are tracked and displayed, including the reason when available.
\item Generated HTML files are XHTML 1.0 Transitional and CSS compliant.
\item Timestamps are self-referencing links.
\item Links on top for quicker navigation: Previous day, Next day, Up.
\item CSS is used for style definition, and a custom CSS file can be used.
\item URLs on messages and subjects are converted to hyperlinks.
\item Timezone used on timestamps is shown on the log files.
\item A custom link can be added on top of page.
\end{itemize}
Options:
\begin{description}
\titem{access\_log}\ind{options!access\_log}
Restricts which users are allowed to enable or disable chatroom logging using ACL and ACCESS.
Default: \term{muc\_admin}.
If you want to allow any chatroom owner put: \term{muc}.
\titem{cssfile}\ind{options!cssfile}
If HTMLs will use a custom CSS file or the embedded one.
Allowed values:
\term{false}: HTMLs will include the standard CSS code;
\term{"CSS-URL"}: the URL of the CSS file (for example: "http://example.com/my.css").
Default: \term{false}.
\titem{dirtype}\ind{options!dirtype}
Type of directory that will be created.
Allowed values:
\term{subdirs}: creates subdirectories for year and month;
\term{plain}: the filename contains the full date, no subdirs.
Default: \term{subdirs}.
\titem{outdir}\ind{options!outdir}
Full path to the directory where html will be generated.
Make sure the system user has write access on that directory.
Default: \term{"www/muc"}.
\titem{timezone}\ind{options!timezone}
What timezone should be used.
Allowed values:
\term{local}: use local time, as reported to Erlang by the operating system;
\term{universal}: use GMT/UTC time.
Default: \term{local}.
\titem{top\_link}\ind{options!top\_link}
Customizable link on top right corner. Syntax of this option: \term{\{"URL", "Text"\}}.
Default: \term{\{"/", "Home"\}}.
\end{description}
Example configuration:
\begin{verbatim}
{modules,
[
...
{mod_muc_log, [
{access_log, muc},
{cssfile, "http://example.com/my.css"},
{dirtype, plain},
{outdir, "/var/www/muclogs"},
{timezone, universal},
{top_link, {"http://www.jabber.ru", "Jabber.ru"}}
]},
...
]}.
\end{verbatim}
\subsection{\modoffline{}}
\label{sec:modoffline}
\ind{modules!\modoffline{}}
@@ -1995,6 +2304,14 @@ figure~\ref{fig:webadmmainru} with figure~\ref{fig:webadmmain})
\subsection{ejabberd 0.9.8}
\verbatiminput{release_notes_0.9.8.txt}
\subsection{ejabberd 1.0.0}
\verbatiminput{release_notes_1.0.0.txt}
\subsection{ejabberd 1.1.0}
\verbatiminput{release_notes_1.1.0.txt}
\subsection{ejabberd 1.1.1}
\verbatiminput{release_notes_1.1.1.txt}
\section{Acknowledgements}
\label{sec:acknowledgements}
@@ -2004,7 +2321,7 @@ Thanks to all people who contributed to this guide:
\item Alexey Shchepin (\ahrefurl{xmpp:aleksey@jabber.ru})
\item Florian Zumbiehl (\ahrefurl{xmpp:florz@florz.de})
\item Michael Grigutsch (\ahrefurl{xmpp:migri@jabber.i-pobox.net})
\item Micka\"el R\'emond (\ahrefurl{xmpp:mremond@erlang-projects.org})
\item Mickael Remond (\ahrefurl{xmpp:mremond@erlang-projects.org})
\item Sander Devrieze (\ahrefurl{xmpp:sander@devrieze.dyndns.org})
\item Sergei Golovan (\ahrefurl{xmpp:sgolovan@nes.ru})
\item Vsevolod Pelipas (\ahrefurl{xmpp:vsevoload@jabber.ru})
+31 -38
View File
@@ -6,7 +6,7 @@ Joeri}
\ejabberd{} is a free (GPL) distributed fault-tolerant \Jabber{}/XMPP server and is mainly written in \footahref{http://www.erlang.org/}{Erlang}.
\ejabberd{} is designed to be a \marking{stable} and \marking{feature rich} \Jabber{}/XMPP server.
\ejabberd{} is designed to be a \marking{stable}, \marking{standards compliant}, and \marking{\mbox{feature rich}} \Jabber{}/XMPP server.
\ejabberd{} is suitable for small servers, whether they need to be scalable or not, as well as extremely big servers.
@@ -41,39 +41,36 @@ Peter Saint-Andr\'e, Executive Director of the Jabber Software Foundation}
\ejabberd{} is:
\begin{itemize}
\item \marking{Multiplatform:} \ejabberd{} runs under Windows NT/2000/XP and Unix derived systems such as Linux, FreeBSD and NetBSD.
\item \marking{Multiplatform:} \ejabberd{} runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.
\item \marking{Distributed:} You can run \ejabberd{} on a cluster of machines and all of them will serve one \Jabber{} domain. When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support hundreds of concurrent users.
\item \marking{Distributed:} You can run \ejabberd{} on a cluster of machines and all of them will serve the same \Jabber{} domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.
\item \marking{Fault-tolerant:} You can deploy an \ejabberd{} cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced ``on the fly''.
\item \marking{Administrator Friendly:} \ejabberd{} is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already installed, and ready to run out of the box. Other benefits for administrators include:
\item \marking{Administrator Friendly:} \ejabberd{} is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
\begin{itemize}
\item Comprehensive documentation.\moreinfo{ --- You can start in the \footahref{http://ejabberd.jabber.ru/book}{ejabberd Book}.}
\item Straightforward installers for Windows and Linux.\moreinfo{ --- (\footahref{http://ejabberd.jabber.ru/screenshots-linux-installer}{Screenshots}).}
\item Straightforward installers for Linux, Mac OS X, and Windows.\improved{}\moreinfo{ --- (\footahref{http://ejabberd.jabber.ru/screenshots-linux-installer}{Screenshots}).}
\item Web interface for administration tasks.\moreinfo{ --- With HTTPS secure access. \footahref{http://ejabberd.jabber.ru/online-demo-webadmin}{Demo}.}
\item Shared Roster groups.\improved{}\moreinfo{ --- The administrator can setup a common list of \Jabber{} users for all users on the server. Those users are virtually added to all rosters. They cannot be removed, but can be renamed or moved into different roster groups. Does not require client implementation. Not related to \jepref{0144} (Roster Item Exchange).\footahref{http://ejabberd.jabber.ru/screenshots-shared-roster-groups}{Screenshots})}
\item Command line administration tool.\moreinfo{ --- Some basic administration tasks can be acomplished using the command line: register/remove users, backup/restore database, amongst others (\footahref{http://ejabberd.jabber.ru/screenshots-administration#ejabberdctl}{Screenshots}).}
\item Shared Roster Groups.\moreinfo{ --- The administrator can setup a common list of \Jabber{} users for all users on the server. Those users are virtually added to all rosters. They cannot be removed, but can be renamed or moved into different roster groups. Does not require client implementation. Not related to \jepref{0144} (Roster Item Exchange).\footahref{http://ejabberd.jabber.ru/screenshots-shared-roster-groups}{Screenshots})}
\item Command line administration tool.\improved{}\moreinfo{ --- Some basic administration tasks can be acomplished using the command line: register/remove users, backup/restore database, amongst others (\footahref{http://ejabberd.jabber.ru/screenshots-administration#ejabberdctl}{Screenshots}).}
\item Can integrate with existing authentication mechanisms.
\item Capability to send announce messages.
\item Statistics via \jepref{0039} (Statistics Gathering).
\item Capability to send announce messages.\improved{}
\end{itemize}
\item \marking{Internationalized:} \ejabberd{} leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
\begin{itemize}
\item Translated in 11 languages.\moreinfo{ --- More information is available \footahref{http://ejabberd.jabber.ru/localization}{here}.}
\item Support for \footahref{http://www.ietf.org/rfc/rfc3490.txt}{IDNA}.
\item Support for \ns{xml:lang}.
\end{itemize}
\item \marking{Modular:} \ejabberd{}'s modular architecture allows easy customization:
\item \marking{Open Standards:} \ejabberd{} is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
\begin{itemize}
\item Load only the modules you want.
\item Extend \ejabberd{} with your own custom modules.\moreinfo{ --- A list of contributed modules and patches is available on the \footahref{http://ejabberd.jabber.ru/contributions}{contributions page}.}
\item Fully XMPP compliant \moreinfo{ --- ejabberd is fully compliant with XMPP Core 1.0 and XMPP IM 1.0. Check the \footahref{http://ejabberd.jabber.ru/protocols}{supported protocols}.}
\item XML-based protocol
\item \footahref{http://ejabberd.jabber.ru/protocols}{Many JEPs supported}.
\end{itemize}
\end{itemize}
\newpage
@@ -87,45 +84,41 @@ Justin Karneges, Founder of the Psi and the Delta projects}
Besides common \Jabber{} server features, \ejabberd{} comes with a wide range of other features:
\begin{itemize}
\item The ability to interface via external components with networks such as:
\item Modular
\begin{itemize}
\item AIM
\item ICQ
\item MSN
\item Yahoo!
\end{itemize}
\item Open Standards
\begin{itemize}
\item \footahref{http://ejabberd.jabber.ru/protocols}{Many JEPs supported}.
\item XML-based protocol
\item Fully XMPP compliant for c2s connections (Core \& IM) \moreinfo{ --- ejabberd supports all XMPP Core 1.0 and XMPP IM 1.0, or is close to it. Check the \footahref{http://ejabberd.jabber.ru/protocols}{supported protocols}.}
\item Load only the modules you want.
\item Extend \ejabberd{} with your own custom modules.\moreinfo{ --- A list of contributed modules and patches is available on the \footahref{http://ejabberd.jabber.ru/contributions}{contributions page}.}
\end{itemize}
\item Security
\begin{itemize}
\item SASL and STARTTLS for c2s connections.
\item STARTTLS and Dialback s2s connections.\new{}
\item Obsolete SSL for c2s connections also supported.
\item SASL and STARTTLS for c2s and s2s connections.\improved{}
\item STARTTLS and Dialback s2s connections.
\item Web interface accessible via HTTPS secure access.
\end{itemize}
\item Databases
\begin{itemize}
\item Native MySQL support.\new{}
\item Native PostgreSQL support.
\item Mnesia.
\item ODBC data storage support (tested against PostgreSQL). \moreinfo{ --- ODBC requests can be load balanced between several connections.}
\item ODBC data storage support. \moreinfo{ --- ODBC requests can be load balanced between several connections.}
\end{itemize}
\item Authentication
\begin{itemize}
\item LDAP. \moreinfo{ --- Accounts can authenticate in a LDAP server.}
\item LDAP and ODBC. \moreinfo{ --- Accounts can authenticate in a LDAP server.}
\item External Authentication script.
\item Internal Authentication.
\end{itemize}
\item Others
\begin{itemize}
\item IPv6 support both for c2s and s2s connections.
\item Support for virtual hosting. \moreinfo{ --- Several \Jabber{} hosts can be hosted on the same \ejabberd{} instance. As simple as adding a new domain name to the list of hosts in the configuration file.}
\item \tjepref{0025}{HTTP Polling} service
\item \tjepref{0045}{Multi-User Chat} module.
\item IRC transport.
\item \tjepref{0060}{Publish-Subscribe} component.
\item Compressing XML streams with Stream Compression (\jepref{0138}).\new{}
\item Interface with networks such as AIM, ICQ and MSN.
\item Statistics via Statistics Gathering (\jepref{0039}).
\item IPv6 support both for c2s and s2s connections.
\item \tjepref{0045}{Multi-User Chat} module with logging.\improved{}
\item Users Directory based on users vCards.
\item \tjepref{0060}{Publish-Subscribe} component.
\item Support for virtual hosting. \moreinfo{ --- Several \Jabber{} hosts can be hosted on the same \ejabberd{} instance. As simple as adding a new domain name to the list of hosts in the configuration file.}
\item \tjepref{0025}{HTTP Polling} service.
\item IRC transport.\improved{}
\end{itemize}
\end{itemize}
+66 -65
View File
@@ -1,112 +1,113 @@
Release notes
Release Notes
ejabberd 1.0.0
2005-12-13
14 December 2005
This document describes the main changes in ejabberd 1.0.0. This version is
compliant with the XMPP (eXtensible Messaging and Presence Protocol)
standard. ejabberd is the first Open Source server claiming to fully
comply to the XMPP standard.
This document describes the main changes in ejabberd 1.0.0. Unique in this
version is the compliancy with the XMPP (eXtensible Messaging and Presence
Protocol) standard. ejabberd is the first Open Source Jabber server claiming
to fully comply to the XMPP standard.
The code can be downloaded from the Process-one website:
ejabberd can be downloaded from the Process-one website:
http://www.process-one.net/en/projects/ejabberd/
For more detailed information, please refer to ejabberd User Guide
on the Process-one website:
Detailed information can be found in the ejabberd Feature Sheet and User
Guide which are available on the Process-one website:
http://www.process-one.net/en/projects/ejabberd/docs.html
Recent changes include....
Recent changes include:
Encrypted server-to-server connections
Server-to-server Encryption for Enhanced Security
- Support for STARTTLS+SASL EXTERNAL for Server-to-Server connections.
- STARTTLS+Dialback has been implemented for Server-to-Server (s2s)
connections. Those options are handled with two new config file options
(s2s_use_starttls and s2s_certfile). See ejabberd.cfg.example for usage
examples.
- DNSName certificate field and DNS name matching are supported.
- Per virtual host certificate can be defined.
- Support for STARTTLS and SASL EXTERNAL to secure server-to-server traffic
has been added.
- Also, STARTTLS and Dialback has been implemented for server-to-server (s2s)
connections. Detailed information about these new features can be found on
http://ejabberd.jabber.ru/s2s-encryption
- commonName and dNSName fields matching were introduced to ease the process
of retrieving certificates.
- Different certificates can be defined for each virtual host.
ODBC Support
- ODBC support has been improved to allow production use of ejabberd with
relational databases.
- Support for VCard storage in ODBC has been added.
- ejd2odbc.erl: is a program that convert an installation from Erlang Mnesia
database to an ODBC relational database.
- Support for vCard storage in ODBC has been added.
- ejd2odbc.erl is a tool to convert an installation from Erlang's database
Mnesia to an ODBC compatible relational database.
Native PostgreSQL support
Native PostgreSQL Support
- Native postgreSQL support has been added: You can now use a PostgreSQL
database without the need to go through the ODBC driver.
- Native PostgreSQL support gives you a better performance when you use
PostgreSQL.
Shared roster
Shared Roster groups
- Shared roster support have been improved: You can specify all users in a
virtual host for addition in a group.
- Shared Roster groups support has been enhanced. New is the ability to add
all registered users to everyone's roster. Detailed information about this
new feature can be found on http://ejabberd.jabber.ru/shared-roster-all
Web interface
Web Interface
- The Web interface internal code has been modified for better integration
and compliance with J-EAI.
- The Web interface is now compliant to XHTML 1.0 Transitional.
- Several bugs have been fixed.
- The web interface internal code has been modified for better integration
and compliancy with J-EAI, an ejabberd-based Enterprise Application
Integration platform.
- More XHTML 1.0 Transitional compliancy work was done.
Transports
- A transport workaround can be enabled with the compile time option. You
- A transport workaround can be enabled during compilation. To do this, you
can pass the "--enable-roster-gateway-workaround" option to the configure
script. For example:
./configure --enable-roster-gateway-workaround
This option allows to add items with subscription "to" in roster by
sending <presence type='subscribed'/> stanza to user. This option is
needed for JIT ICQ transport.
Warning: By using this option, ejabberd is not fully XMPP compliant.
script. (./configure --enable-roster-gateway-workaround)
This option allows transports to add items with subscription "to" in the
roster by sending <presence type='subscribed'/> stanza to user. This option
is only needed for JIT ICQ transport.
Warning: by enabling this option, ejabberd will not be fully XMPP compliant
anymore.
Documentation and translations
Documentation and Internationalization
- Documentation has been improved to cover more topics.
- Translations have been updated to support the new features.
- Documentation has been extended to cover more topics.
- Translations have been updated.
Bugfixes
- This release contains several bugfixes.
Among other bugfixes include improvements to the Client-to-Server
connection management module. Please refer to the ChangeLog file supplied
- Among other bugfixes include improvements to the client-to-server (c2s)
connection management module.
- Please refer to the ChangeLog file supplied
with this release regarding all improvements in ejabberd.
Erlang version supported
- You now need at least Erlang/OTP R9C to be able to run ejabberd 1.0.0.
Installation Notes
Installers
Installers are provided for Microsoft Windows and Linux/x86. The Linux
installer includes Erlang ASN.1 modules for LDAP authentication support.
Installers are available from:
Supported Erlang Version
- You need at least Erlang/OTP R9C to run ejabberd 1.0.0.
Installation
Installers are provided for Microsoft Windows and Linux/x86.
Installers can be retrieved from:
http://www.process-one.net/en/projects/ejabberd/download.html
Migration
Migration Notes
- Before any migration, ejabberd system and database must be properly
backed-up.
- System migrating from a previous ODBC based install will need to change
their relational database schema. The following SQL commands must be run
on the database:
CREATE SEQUENCE spool_seq_seq;
ALTER TABLE spool ADD COLUMN seq integer;
ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq');
UPDATE spool SET seq = DEFAULT;
ALTER TABLE spool ALTER COLUMN seq SET NOT NULL;
backed up.
- When upgrading an ODBC-based installation, you will need to change the
relational database schema. The following SQL commands must be run on the
database:
CREATE SEQUENCE spool_seq_seq;
ALTER TABLE spool ADD COLUMN seq integer;
ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq');
UPDATE spool SET seq = DEFAULT;
ALTER TABLE spool ALTER COLUMN seq SET NOT NULL;
References
The ejabberd feature sheet helps comparing with other Jabber/XMPP
servers:
http://www.process-one.net/en/projects/ejabberd/docs/features.pdf
Contributed tutorials of interest are:
- Migration from Jabberd1.4 to ejabberd:
http://ejabberd.jabber.ru/jabberd1-to-ejabberd
+115
View File
@@ -0,0 +1,115 @@
Release Notes
ejabberd 1.1.0
24 April 2006
This document describes the main changes in ejabberd 1.1.0. This version
introduce new features including support for new Jabber Enhancement
Proposals and several performance improvements enabling deployments on an
even larger scale than already possible.
ejabberd can be downloaded from the Process-one website:
http://www.process-one.net/en/projects/ejabberd/
Detailed information can be found in the ejabberd Feature Sheet and User
Guide which are available on the Process-one website:
http://www.process-one.net/en/projects/ejabberd/docs.html
A complete list of changes is available from:
http://support.process-one.net/secure/ReleaseNote.jspa?projectId=10011&styleName=Html&version=10025
Recent changes include:
New Jabber Enhancement Proposal support:
- JEP-0050: Ad-Hoc Commands.
- JEP-0138: Stream Compression.
- JEP-0175: SASL anonymous.
Anonymous login
- SASL anonymous.
- Anonymous login for clients that do not yet support SASL Anonymous.
Relational database Support
- MySQL is now fully supported through ODBC and in native mode.
- Various improvements to the native database interfaces.
- The migration tool can use relational databases.
Multi-User Chat improvements
- Logging of room discussion to text file is now supported.
- Better reconfiguration support.
- Security oriented fixes.
- Several improvements and updates to latest JEP-0045.
Performance scalability improvements for large clusters
- Improved session synchronisation management between cluster nodes.
- Internal architecture has been reworked to use generalize Erlang/OTP
framework usage.
- Speed improvement on logger.
- TCP/IP packet reception change for better network throttling and
regulation.
As a result, these improvements will reduce load on large scale deployments.
XMPP Protocol related improvements
- XML stanza size can be limited.
- Messages are send to all resources with the same highest priority.
Documentation and Internationalization
- Documentation has been extended to cover more topics.
- Translations have been updated.
Web interface
- XHTML 1.0 compliance.
Bugfixes
- This release contains many bugfixes on various areas such as Publish-Subscribe, build
chain, installers, IRC gateway, ejabberdctl, amongst others.
- Please refer to the ChangeLog file supplied with this release regarding
all improvements in ejabberd.
Installation Notes
Supported Erlang Version
- You need at least Erlang/OTP R9C-2 to run ejabberd 1.1.0.
Installation
Installers are provided for Microsoft Windows, Linux/x86 and MacOSX/PPC.
Installers can be retrieved from:
http://www.process-one.net/en/projects/ejabberd/download.html
Migration Notes
- Before any migration, ejabberd system and database must be properly
backed up.
- The database schema has not been changed comparing to version 1.0.0 and
consequently it does not require any migration.
References
Contributed tutorials and documents of interest are:
- Migration from Jabberd1.4, Jabberd2 and WPJabber to ejabberd:
http://ejabberd.jabber.ru/migrate-to-ejabberd
- Transport configuration for connecting to other networks:
http://ejabberd.jabber.ru/tutorials-transports
- Using ejabberd with MySQL native driver:
http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver
- Anonymous User Support:
http://support.process-one.net/doc/display/MESSENGER/Anonymous+users+support
- Frequently Asked Questions:
http://ejabberd.jabber.ru/faq
END
+119
View File
@@ -0,0 +1,119 @@
Release Notes
ejabberd 1.1.1
28 April 2006
This document describes the main changes in ejabberd 1.1.x. This version
introduce new features including support for new Jabber Enhancement
Proposals and several performance improvements enabling deployments on an
even larger scale than already possible.
This release fix a security issue introduced in ejabberd 1.1.0. In SASL
mode, anonymous login was enabled as a default. Upgrading ejabberd 1.1.0 to
ejabberd 1.1.1 is highly recommanded.
ejabberd can be downloaded from the Process-one website:
http://www.process-one.net/en/projects/ejabberd/
Detailed information can be found in the ejabberd Feature Sheet and User
Guide which are available on the Process-one website:
http://www.process-one.net/en/projects/ejabberd/docs.html
A complete list of changes is available from:
http://support.process-one.net/secure/ReleaseNote.jspa?projectId=10011&styleName=Html&version=10025
Recent changes include:
New Jabber Enhancement Proposal support:
- JEP-0050: Ad-Hoc Commands.
- JEP-0138: Stream Compression.
- JEP-0175: SASL anonymous.
Anonymous login
- SASL anonymous.
- Anonymous login for clients that do not yet support SASL Anonymous.
Relational database Support
- MySQL is now fully supported through ODBC and in native mode.
- Various improvements to the native database interfaces.
- The migration tool can use relational databases.
Multi-User Chat improvements
- Logging of room discussion to text file is now supported.
- Better reconfiguration support.
- Security oriented fixes.
- Several improvements and updates to latest JEP-0045.
Performance scalability improvements for large clusters
- Improved session synchronisation management between cluster nodes.
- Internal architecture has been reworked to use generalize Erlang/OTP
framework usage.
- Speed improvement on logger.
- TCP/IP packet reception change for better network throttling and
regulation.
As a result, these improvements will reduce load on large scale deployments.
XMPP Protocol related improvements
- XML stanza size can be limited.
- Messages are send to all resources with the same highest priority.
Documentation and Internationalization
- Documentation has been extended to cover more topics.
- Translations have been updated.
Web interface
- XHTML 1.0 compliance.
Bugfixes
- This release contains many bugfixes on various areas such as Publish-Subscribe, build
chain, installers, IRC gateway, ejabberdctl, amongst others.
- Please refer to the ChangeLog file supplied with this release regarding
all improvements in ejabberd.
Installation Notes
Supported Erlang Version
- You need at least Erlang/OTP R9C-2 to run ejabberd 1.1.0.
Installation
Installers are provided for Microsoft Windows, Linux/x86 and MacOSX/PPC.
Installers can be retrieved from:
http://www.process-one.net/en/projects/ejabberd/download.html
Migration Notes
- Before any migration, ejabberd system and database must be properly
backed up.
- The database schema has not been changed comparing to version 1.0.0 and
consequently it does not require any migration.
References
Contributed tutorials and documents of interest are:
- Migration from Jabberd1.4, Jabberd2 and WPJabber to ejabberd:
http://ejabberd.jabber.ru/migrate-to-ejabberd
- Transport configuration for connecting to other networks:
http://ejabberd.jabber.ru/tutorials-transports
- Using ejabberd with MySQL native driver:
http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver
- Anonymous User Support:
http://support.process-one.net/doc/display/MESSENGER/Anonymous+users+support
- Frequently Asked Questions:
http://ejabberd.jabber.ru/faq
END
+1 -1
View File
@@ -1,2 +1,2 @@
% Define ejabberd version here.
\newcommand{\version}{1.0.0}
\newcommand{\version}{1.1.1}
+10 -5
View File
@@ -17,26 +17,30 @@ ifdef debug
ERLC_FLAGS+=+debug_info
endif
ifdef ejabberd_debug
ERLC_FLAGS+=-Dejabberd_debug
endif
ifeq (@roster_gateway_workaround@, true)
ERLC_FLAGS+=-DROSTER_GATEWAY_WORKAROUND
endif
prefix = @prefix@
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@
ERLSHLIBS = expat_erl.so
SOURCES = $(wildcard *.erl)
BEAMS = $(SOURCES:.erl=.beam)
DESTDIR =
DESTDIR =
EJABBERDDIR = $(DESTDIR)/var/lib/ejabberd
EJABBERDDIR = $(DESTDIR)@prefix@/var/lib/ejabberd
BEAMDIR = $(EJABBERDDIR)/ebin
PRIVDIR = $(EJABBERDDIR)/priv
SODIR = $(PRIVDIR)/lib
MSGSDIR = $(PRIVDIR)/msgs
LOGDIR = $(DESTDIR)/var/log/ejabberd
ETCDIR = $(DESTDIR)/etc/ejabberd
LOGDIR = $(DESTDIR)@prefix@/var/log/ejabberd
ETCDIR = $(DESTDIR)@prefix@/etc/ejabberd
ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj
@@ -85,6 +89,7 @@ clean: clean-recursive clean-local
clean-local:
rm -f *.beam $(ERLSHLIBS)
rm -f XmppAddr.asn1db XmppAddr.erl XmppAddr.hrl
distclean: distclean-recursive clean-local
rm -f config.status
+17 -2
View File
@@ -65,10 +65,15 @@ release : build release_clean
copy stringprep\*.tcl $(SRC_DIR)\stringprep
mkdir $(SRC_DIR)\tls
copy tls\*.erl $(SRC_DIR)\tls
copy tls\*.c $(SRC_DIR)\tls
mkdir $(SRC_DIR)\ejabberd_zlib
copy ejabberd_zlib\*.erl $(SRC_DIR)\ejabberd_zlib
copy ejabberd_zlib\*.c $(SRC_DIR)\ejabberd_zlib
mkdir $(SRC_DIR)\web
copy web\*.erl $(SRC_DIR)\web
mkdir $(SRC_DIR)\odbc
copy odbc\*.erl $(SRC_DIR)\odbc
copy odbc\*.sql $(EREL)
mkdir $(DOC_DIR)
copy ..\doc\*.txt $(DOC_DIR)
copy ..\doc\*.html $(DOC_DIR)
@@ -93,14 +98,19 @@ all-recursive :
nmake -nologo -f Makefile.win32
cd ..\tls
nmake -nologo -f Makefile.win32
cd ..\ejabberd_zlib
nmake -nologo -f Makefile.win32
cd ..\web
nmake -nologo -f Makefile.win32
cd ..\odbc
nmake -nologo -f Makefile.win32
cd ..
compile-beam :
erlc *.erl
compile-beam : XmppAddr.hrl
erl -s make all report -noinput -s erlang halt
XmppAddr.hrl : XmppAddr.asn1
erlc -bber_bin +der +compact_bit_string +optimize +noobj XmppAddr.asn1
CLEAN : clean-recursive clean-local
@@ -110,6 +120,9 @@ clean-local :
-@erase expat_erl.exp
-@erase expat_erl.lib
-@erase *.beam
-@erase XmppAddr.asn1db
-@erase XmppAddr.erl
-@erase XmppAddr.hrl
clean-recursive :
cd eldap
@@ -124,6 +137,8 @@ clean-recursive :
nmake -nologo -f Makefile.win32 clean
cd ..\tls
nmake -nologo -f Makefile.win32 clean
cd ..\ejabberd_zlib
nmake -nologo -f Makefile.win32 clean
cd ..\web
nmake -nologo -f Makefile.win32 clean
cd ..\odbc
+32 -1
View File
@@ -29,6 +29,37 @@ AC_DEFUN(AM_WITH_EXPAT,
AC_SUBST(EXPAT_LIBS)
])
AC_DEFUN(AM_WITH_ZLIB,
[ AC_ARG_WITH(zlib,
[ --with-zlib=PREFIX prefix where zlib is installed])
ZLIB_CFLAGS=
ZLIB_LIBS=
if test x"$with_zlib" != x; then
ZLIB_CFLAGS="-I$with_zlib/include"
ZLIB_LIBS="-L$with_zlib/lib"
fi
AC_CHECK_LIB(z, gzgets,
[ ZLIB_LIBS="$ZLIB_LIBS -lz"
zlib_found=yes ],
[ zlib_found=no ],
"$ZLIB_LIBS")
if test $zlib_found = no; then
AC_MSG_ERROR([Could not find the zlib library])
fi
zlib_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
AC_CHECK_HEADERS(zlib.h, , zlib_found=no)
if test $zlib_found = no; then
AC_MSG_ERROR([Could not find zlib.h])
fi
CFLAGS="$zlib_save_CFLAGS"
AC_SUBST(ZLIB_CFLAGS)
AC_SUBST(ZLIB_LIBS)
])
AC_DEFUN(AM_WITH_ERLANG,
[ AC_ARG_WITH(erlang,
[ --with-erlang=PREFIX path to erlc and erl ])
@@ -37,7 +68,7 @@ AC_DEFUN(AM_WITH_ERLANG,
AC_PATH_TOOL(ERLC, erlc, , $PATH:$with_erlang:$with_erlang/bin)
AC_PATH_TOOL(ERL, erl, , $PATH:$with_erlang:$with_erlang/bin)
if test "z$ERLC" == "z" || test "z$ERL" == "z"; then
if test "z$ERLC" = "z" || test "z$ERL" = "z"; then
AC_MSG_ERROR([erlang not found])
fi
+111
View File
@@ -0,0 +1,111 @@
%%%----------------------------------------------------------------------
%%% File : adhoc.erl
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
%%% Purpose : Provide helper functions for ad-hoc commands (JEP-0050)
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(adhoc).
-author('henoch@dtek.chalmers.se').
-vsn('$Revision$ ').
-export([parse_request/1,
produce_response/2,
produce_response/1]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").
%% Parse an ad-hoc request. Return either an adhoc_request record or
%% an {error, ErrorType} tuple.
parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) ->
?DEBUG("entering parse_request...", []),
Node = xml:get_tag_attr_s("node", SubEl),
SessionID = xml:get_tag_attr_s("sessionid", SubEl),
Action = xml:get_tag_attr_s("action", SubEl),
XData = find_xdata_el(SubEl),
{xmlelement, _, _, AllEls} = SubEl,
if XData ->
Others = lists:delete(XData, AllEls);
true ->
Others = AllEls
end,
#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID,
action = Action,
xdata = XData,
others = Others};
parse_request(_) ->
{error, ?ERR_BAD_REQUEST}.
%% Borrowed from mod_vcard.erl
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).
%% Produce a <command/> node to use as response from an adhoc_response
%% record, filling in values for language, node and session id from
%% the request.
produce_response(#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID},
Response) ->
produce_response(Response#adhoc_response{lang = Lang,
node = Node,
sessionid = SessionID}).
%% Produce a <command/> node to use as response from an adhoc_response
%% record.
produce_response(#adhoc_response{lang = _Lang,
node = Node,
sessionid = ProvidedSessionID,
status = Status,
defaultaction = DefaultAction,
actions = Actions,
notes = Notes,
elements = Elements}) ->
SessionID = if is_list(ProvidedSessionID), ProvidedSessionID /= "" ->
ProvidedSessionID;
true ->
jlib:now_to_utc_string(now())
end,
case Actions of
[] ->
ActionsEls = [];
_ ->
case DefaultAction of
"" ->
ActionsElAttrs = [];
_ ->
ActionsElAttrs = [{"execute", DefaultAction}]
end,
ActionsEls = [{xmlelement, "actions",
ActionsElAttrs,
[{xmlelement, Action, [], []} || Action <- Actions]}]
end,
NotesEls = lists:map(fun({Type, Text}) ->
{xmlelement, "note",
[{"type", Type}],
[{xmlcdata, Text}]}
end, Notes),
{xmlelement, "command",
[{"xmlns", ?NS_COMMANDS},
{"sessionid", SessionID},
{"node", Node},
{"status", atom_to_list(Status)}],
ActionsEls ++ NotesEls ++ Elements}.
+15
View File
@@ -0,0 +1,15 @@
-record(adhoc_request, {lang,
node,
sessionid,
action,
xdata,
others}).
-record(adhoc_response, {lang,
node,
sessionid,
status,
defaultaction = "",
actions = [],
notes = [],
elements = []}).
+335 -29
View File
@@ -310,7 +310,8 @@ ac_includes_default="\
# include <unistd.h>
#endif"
ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT SET_MAKE ERLC ac_pt_ERLC ERL ac_pt_ERL ERLANG_CFLAGS ERLANG_LIBS LIBICONV CPP EGREP EXPAT_CFLAGS EXPAT_LIBS LIBOBJS mod_pubsub make_mod_pubsub mod_irc make_mod_irc mod_muc make_mod_muc eldap make_eldap web make_web tls make_tls odbc make_odbc roster_gateway_workaround SSL_LIBS SSL_CFLAGS LTLIBOBJS'
ac_default_prefix=
ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT SET_MAKE ERLC ac_pt_ERLC ERL ac_pt_ERL ERLANG_CFLAGS ERLANG_LIBS LIBICONV CPP EGREP EXPAT_CFLAGS EXPAT_LIBS ZLIB_CFLAGS ZLIB_LIBS LIBOBJS mod_pubsub make_mod_pubsub mod_irc make_mod_irc mod_muc make_mod_muc eldap make_eldap web make_web tls make_tls odbc make_odbc ejabberd_zlib make_ejabberd_zlib roster_gateway_workaround SSL_LIBS SSL_CFLAGS LTLIBOBJS'
ac_subst_files=''
# Initialize some variables set by options.
@@ -850,6 +851,7 @@ Optional Features:
--enable-web enable web (default: yes)
--enable-tls enable tls (default: yes)
--enable-odbc enable odbc (default: no)
--enable-ejabberd_zlib enable ejabberd_zlib (default: yes)
--enable-roster-gateway-workaround Turn on workaround for processing gateway subscriptions
Optional Packages:
@@ -858,6 +860,7 @@ Optional Packages:
--with-erlang=PREFIX path to erlc and erl
--with-libiconv-prefix=PREFIX prefix where libiconv is installed
--with-expat=PREFIX prefix where EXPAT is installed
--with-zlib=PREFIX prefix where zlib is installed
--with-openssl=PREFIX prefix where OPENSSL is installed
Some influential environment variables:
@@ -961,7 +964,7 @@ esac
else
echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
fi
cd "$ac_popdir"
cd $ac_popdir
done
fi
@@ -1888,7 +1891,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -1946,7 +1950,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -2062,7 +2067,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -2116,7 +2122,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -2161,7 +2168,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -2205,7 +2213,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -2462,7 +2471,7 @@ else
fi
if test "z$ERLC" == "z" || test "z$ERL" == "z"; then
if test "z$ERLC" = "z" || test "z$ERL" = "z"; then
{ { echo "$as_me:$LINENO: error: erlang not found" >&5
echo "$as_me: error: erlang not found" >&2;}
{ (exit 1); exit 1; }; }
@@ -2598,7 +2607,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -2648,7 +2658,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -2705,7 +2716,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -2784,7 +2796,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -3108,7 +3121,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -3278,7 +3292,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -3363,7 +3378,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -3436,7 +3452,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -3563,6 +3580,261 @@ echo "$as_me: error: Could not find expat.h" >&2;}
#locating zlib
# Check whether --with-zlib or --without-zlib was given.
if test "${with_zlib+set}" = set; then
withval="$with_zlib"
fi;
ZLIB_CFLAGS=
ZLIB_LIBS=
if test x"$with_zlib" != x; then
ZLIB_CFLAGS="-I$with_zlib/include"
ZLIB_LIBS="-L$with_zlib/lib"
fi
echo "$as_me:$LINENO: checking for gzgets in -lz" >&5
echo $ECHO_N "checking for gzgets in -lz... $ECHO_C" >&6
if test "${ac_cv_lib_z_gzgets+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lz "$ZLIB_LIBS" $LIBS"
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end 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 gzgets ();
int
main ()
{
gzgets ();
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext conftest$ac_exeext
if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
(eval $ac_link) 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (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); }; } &&
{ 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_z_gzgets=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_cv_lib_z_gzgets=no
fi
rm -f conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
echo "$as_me:$LINENO: result: $ac_cv_lib_z_gzgets" >&5
echo "${ECHO_T}$ac_cv_lib_z_gzgets" >&6
if test $ac_cv_lib_z_gzgets = yes; then
ZLIB_LIBS="$ZLIB_LIBS -lz"
zlib_found=yes
else
zlib_found=no
fi
if test $zlib_found = no; then
{ { echo "$as_me:$LINENO: error: Could not find the zlib library" >&5
echo "$as_me: error: Could not find the zlib library" >&2;}
{ (exit 1); exit 1; }; }
fi
zlib_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
for ac_header in zlib.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
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end 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>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (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); }; } &&
{ 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
sed 's/^/| /' conftest.$ac_ext >&5
ac_header_compiler=no
fi
rm -f conftest.err 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
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end 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=$?
grep -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
ac_cpp_err=$ac_cpp_err$ac_c_werror_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
sed 's/^/| /' 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:$ac_c_preproc_warn_flag 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 compiler's result" >&5
echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
ac_header_preproc=yes
;;
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: see the Autoconf documentation" >&5
echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
{ echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&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;}
{ echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
(
cat <<\_ASBOX
## --------------------------------- ##
## Report this to BUG-REPORT-ADDRESS ##
## --------------------------------- ##
_ASBOX
) |
sed "s/^/$as_me: WARNING: /" >&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
else
zlib_found=no
fi
done
if test $zlib_found = no; then
{ { echo "$as_me:$LINENO: error: Could not find zlib.h" >&5
echo "$as_me: error: Could not find zlib.h" >&2;}
{ (exit 1); exit 1; }; }
fi
CFLAGS="$zlib_save_CFLAGS"
# Checks for typedefs, structures, and compiler characteristics.
echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
@@ -3640,7 +3912,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -3672,6 +3945,9 @@ _ACEOF
fi
# Change default prefix
/
# Checks for library functions.
for ac_header in stdlib.h
@@ -3707,7 +3983,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -3936,7 +4213,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -4224,6 +4502,28 @@ echo "${ECHO_T}$mr_enable_odbc" >&6
ejabberd_zlib=
make_ejabberd_zlib=
echo "$as_me:$LINENO: checking whether build ejabberd_zlib" >&5
echo $ECHO_N "checking whether build ejabberd_zlib... $ECHO_C" >&6
# Check whether --enable-ejabberd_zlib or --disable-ejabberd_zlib was given.
if test "${enable_ejabberd_zlib+set}" = set; then
enableval="$enable_ejabberd_zlib"
mr_enable_ejabberd_zlib="$enableval"
else
mr_enable_ejabberd_zlib=yes
fi;
if test "$mr_enable_ejabberd_zlib" = "yes"; then
ejabberd_zlib=ejabberd_zlib
make_ejabberd_zlib=ejabberd_zlib/Makefile
fi
echo "$as_me:$LINENO: result: $mr_enable_ejabberd_zlib" >&5
echo "${ECHO_T}$mr_enable_ejabberd_zlib" >&6
# Check whether --enable-roster_gateway_workaround or --disable-roster_gateway_workaround was given.
if test "${enable_roster_gateway_workaround+set}" = set; then
enableval="$enable_roster_gateway_workaround"
@@ -4239,7 +4539,7 @@ else
fi;
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub $make_eldap $make_web stringprep/Makefile $make_tls $make_odbc"
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub $make_eldap $make_web stringprep/Makefile $make_tls $make_odbc $make_ejabberd_zlib"
#openssl
@@ -4294,7 +4594,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -4362,7 +4663,8 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@@ -5056,6 +5358,7 @@ do
"stringprep/Makefile" ) CONFIG_FILES="$CONFIG_FILES stringprep/Makefile" ;;
"$make_tls" ) CONFIG_FILES="$CONFIG_FILES $make_tls" ;;
"$make_odbc" ) CONFIG_FILES="$CONFIG_FILES $make_odbc" ;;
"$make_ejabberd_zlib" ) CONFIG_FILES="$CONFIG_FILES $make_ejabberd_zlib" ;;
*) { { 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; }; };;
@@ -5158,6 +5461,8 @@ s,@CPP@,$CPP,;t t
s,@EGREP@,$EGREP,;t t
s,@EXPAT_CFLAGS@,$EXPAT_CFLAGS,;t t
s,@EXPAT_LIBS@,$EXPAT_LIBS,;t t
s,@ZLIB_CFLAGS@,$ZLIB_CFLAGS,;t t
s,@ZLIB_LIBS@,$ZLIB_LIBS,;t t
s,@LIBOBJS@,$LIBOBJS,;t t
s,@mod_pubsub@,$mod_pubsub,;t t
s,@make_mod_pubsub@,$make_mod_pubsub,;t t
@@ -5173,6 +5478,8 @@ s,@tls@,$tls,;t t
s,@make_tls@,$make_tls,;t t
s,@odbc@,$odbc,;t t
s,@make_odbc@,$make_odbc,;t t
s,@ejabberd_zlib@,$ejabberd_zlib,;t t
s,@make_ejabberd_zlib@,$make_ejabberd_zlib,;t t
s,@roster_gateway_workaround@,$roster_gateway_workaround,;t t
s,@SSL_LIBS@,$SSL_LIBS,;t t
s,@SSL_CFLAGS@,$SSL_CFLAGS,;t t
@@ -5339,6 +5646,11 @@ esac
if test x"$ac_file" != x-; then
{ echo "$as_me:$LINENO: creating $ac_file" >&5
echo "$as_me: creating $ac_file" >&6;}
rm -f "$ac_file"
fi
# Let's still pretend it is `configure' which instantiates (i.e., don't
# use $as_me), people would be surprised to read:
# /* config.h. Generated by config.status. */
@@ -5377,12 +5689,6 @@ echo "$as_me: error: cannot find input file: $f" >&2;}
fi;;
esac
done` || { (exit 1); exit 1; }
if test x"$ac_file" != x-; then
{ echo "$as_me:$LINENO: creating $ac_file" >&5
echo "$as_me: creating $ac_file" >&6;}
rm -f "$ac_file"
fi
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
sed "$ac_vpsub
+8 -1
View File
@@ -14,10 +14,15 @@ AM_WITH_ERLANG
AM_ICONV
#locating libexpat
AM_WITH_EXPAT
#locating zlib
AM_WITH_ZLIB
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
# Change default prefix
AC_PREFIX_DEFAULT /
# Checks for library functions.
AC_FUNC_MALLOC
AC_HEADER_STDC
@@ -29,6 +34,7 @@ AC_MOD_ENABLE(eldap, yes)
AC_MOD_ENABLE(web, yes)
AC_MOD_ENABLE(tls, yes)
AC_MOD_ENABLE(odbc, no)
AC_MOD_ENABLE(ejabberd_zlib, yes)
AC_ARG_ENABLE(roster_gateway_workaround,
[ --enable-roster-gateway-workaround Turn on workaround for processing gateway subscriptions],
@@ -47,7 +53,8 @@ AC_CONFIG_FILES([Makefile
$make_web
stringprep/Makefile
$make_tls
$make_odbc])
$make_odbc
$make_ejabberd_zlib])
#openssl
AM_WITH_OPENSSL
AC_OUTPUT
+12 -4
View File
@@ -28,14 +28,19 @@ start() ->
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";
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\iconv.lib\n",
ZlibDir = "ZLIB_DIR = c:\\progra~1\\zlib-1.2.3\n",
ZlibLib = "ZLIB_LIB = $(ZLIB_DIR)\\lib\\zlib.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"
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\iconv.lib\n",
ZlibDir = "ZLIB_DIR = c:\\progra~1\\zlib-1.2.3\n",
ZlibLib = "ZLIB_LIB = $(ZLIB_DIR)\\lib\\zlib1.lib\n"
end,
EVersion = "ERLANG_VERSION = " ++ erlang:system_info(version) ++ "\n",
EIDirS = "EI_DIR = " ++ code:lib_dir("erl_interface") ++ "\n",
RootDirS = "ERLANG_DIR = " ++ code:root_dir() ++ "\n",
Version = "EJABBERD_VERSION = " ++ ?VERSION ++ "\n",
@@ -46,7 +51,8 @@ start() ->
StdLibDir = "STDLIBDIR = " ++ code:lib_dir("stdlib") ++ "\n",
file:write_file("Makefile.inc",
list_to_binary(EIDirS ++
list_to_binary(EVersion ++
EIDirS ++
RootDirS ++
Version ++
SSLDir ++
@@ -56,7 +62,9 @@ start() ->
ExpatLib ++
ExpatFlag ++
IconvDir ++
IconvLib)),
IconvLib ++
ZlibDir ++
ZlibLib)),
halt().
+37 -22
View File
@@ -25,8 +25,7 @@
-export([behaviour_info/1]).
behaviour_info(callbacks) ->
[{mech_new, 2},
{mech_step, 2}];
[{mech_new, 3}, {mech_step, 2}];
behaviour_info(Other) ->
undefined.
@@ -36,6 +35,7 @@ start() ->
{keypos, #sasl_mechanism.mechanism}]),
cyrsasl_plain:start([]),
cyrsasl_digest:start([]),
cyrsasl_anonymous:start([]),
ok.
register_mechanism(Mechanism, Module, RequirePlainPassword) ->
@@ -81,18 +81,19 @@ check_credentials(State, Props) ->
listmech(Host) ->
RequirePlainPassword = ejabberd_auth:plain_password_required(Host),
ets:select(sasl_mechanism,
[{#sasl_mechanism{mechanism = '$1',
require_plain_password = '$2',
_ = '_'},
if
RequirePlainPassword ->
[{'==', '$2', false}];
true ->
[]
end,
['$1']}]).
Mechs = ets:select(sasl_mechanism,
[{#sasl_mechanism{mechanism = '$1',
require_plain_password = '$2',
_ = '_'},
if
RequirePlainPassword ->
[{'==', '$2', false}];
true ->
[]
end,
['$1']}]),
filter_anonymous(Host, Mechs).
server_new(Service, ServerFQDN, UserRealm, SecFlags,
GetPassword, CheckPassword) ->
@@ -103,14 +104,21 @@ server_new(Service, ServerFQDN, UserRealm, SecFlags,
check_password = CheckPassword}.
server_start(State, Mech, ClientIn) ->
case ets:lookup(sasl_mechanism, Mech) of
[#sasl_mechanism{module = Module}] ->
{ok, MechState} = Module:mech_new(State#sasl_state.get_password,
State#sasl_state.check_password),
server_step(State#sasl_state{mech_mod = Module,
mech_state = MechState},
ClientIn);
_ ->
case lists:member(Mech, listmech(State#sasl_state.myname)) of
true ->
case ets:lookup(sasl_mechanism, Mech) of
[#sasl_mechanism{module = Module}] ->
{ok, MechState} = Module:mech_new(
State#sasl_state.myname,
State#sasl_state.get_password,
State#sasl_state.check_password),
server_step(State#sasl_state{mech_mod = Module,
mech_state = MechState},
ClientIn);
_ ->
{error, "no-mechanism"}
end;
false ->
{error, "no-mechanism"}
end.
@@ -132,3 +140,10 @@ server_step(State, ClientIn) ->
{error, Error}
end.
%% Remove the anonymous mechanism from the list if not enabled for the given
%% host
filter_anonymous(Host, Mechs) ->
case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(Host) of
true -> Mechs;
false -> Mechs -- ["ANONYMOUS"]
end.
+39
View File
@@ -0,0 +1,39 @@
%%%----------------------------------------------------------------------
%%% File : cyrsasl_anonymous.erl
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
%%% Purpose : ANONYMOUS SASL mechanism
%%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% Id : $Id$
%%%----------------------------------------------------------------------
%% See http://www.ietf.org/internet-drafts/draft-ietf-sasl-anon-05.txt
-module(cyrsasl_anonymous).
-vsn('$Revision$').
-export([start/1, stop/0, mech_new/3, mech_step/2]).
-behaviour(cyrsasl).
-record(state, {server}).
start(_Opts) ->
cyrsasl:register_mechanism("ANONYMOUS", ?MODULE, false),
ok.
stop() ->
ok.
mech_new(Host, _GetPassword, _CheckPassword) ->
{ok, #state{server = Host}}.
mech_step(State, _ClientIn) ->
%% We generate a random username:
User = lists:concat([randoms:get_string() | tuple_to_list(now())]),
Server = State#state.server,
%% Checks that the username is available
case ejabberd_auth:is_user_exists(User, Server) of
true -> {error, "not-authorized"};
false -> {ok, [{username, User}]}
end.
+2 -2
View File
@@ -12,7 +12,7 @@
-export([start/1,
stop/0,
mech_new/2,
mech_new/3,
mech_step/2]).
-behaviour(cyrsasl).
@@ -25,7 +25,7 @@ start(_Opts) ->
stop() ->
ok.
mech_new(GetPassword, _CheckPassword) ->
mech_new(_Host, GetPassword, _CheckPassword) ->
{ok, #state{step = 1,
nonce = randoms:get_string(),
get_password = GetPassword}}.
+2 -2
View File
@@ -10,7 +10,7 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-export([start/1, stop/0, mech_new/2, mech_step/2, parse/1]).
-export([start/1, stop/0, mech_new/3, mech_step/2, parse/1]).
-behaviour(cyrsasl).
@@ -23,7 +23,7 @@ start(_Opts) ->
stop() ->
ok.
mech_new(_GetPassword, CheckPassword) ->
mech_new(_Host, _GetPassword, CheckPassword) ->
{ok, #state{check_password = CheckPassword}}.
mech_step(State, ClientIn) ->
+24 -8
View File
@@ -73,11 +73,11 @@
{access, local, [{allow, local}]}.
% Authentification method. If you want to use internal user base, then use
% Authentication method. If you want to use internal user base, then use
% this line:
{auth_method, internal}.
% For LDAP authentification use these lines instead of above one:
% For LDAP authentication 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
@@ -85,11 +85,11 @@
%{ldap_rootdn, "dc=example,dc=com"}. % LDAP manager
%{ldap_password, "******"}. % Password to LDAP manager
% For authentification via external script use the following:
% For authentication via external script use the following:
%{auth_method, external}.
%{extauth_program, "/path/to/authentification/script"}.
%{extauth_program, "/path/to/authentication/script"}.
% For authentification via ODBC use the following:
% For authentication via ODBC use the following:
%{auth_method, odbc}.
%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}.
@@ -97,6 +97,15 @@
% Host name:
{hosts, ["localhost"]}.
%% Anonymous login support:
%% auth_method: anonymous
%% anonymous_protocol: sasl_anon|login_anon|both
%% allow_multiple_connections: true|false
%%{host_config, "public.example.org", [{auth_method, anonymous},
%% {allow_multiple_connections, false},
%% {anonymous_protocol, sasl_anon}]}.
%% To use both anonymous and internal authentication:
%%{host_config, "public.example.org", [{auth_method, [anonymous, internal]}]}.
% Default language for server messages
{language, "en"}.
@@ -104,13 +113,17 @@
% Listened ports:
{listen,
[{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper},
{max_stanza_size, 65536},
starttls, {certfile, "./ssl.pem"}]},
{5223, ejabberd_c2s, [{access, c2s},
{max_stanza_size, 65536},
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}]},
{5269, ejabberd_s2s_in, [{shaper, s2s_shaper},
{max_stanza_size, 131072}
]},
{5280, ejabberd_http, [http_poll, web_admin]},
{8888, ejabberd_service, [{access, all},
{hosts, ["icq.localhost", "sms.localhost"],
@@ -134,13 +147,14 @@
{mod_register, [{access, register}]},
{mod_roster, []},
{mod_privacy, []},
{mod_configure, []},
{mod_adhoc, []},
{mod_configure, []}, % Depends on mod_adhoc
{mod_configure2, []},
{mod_disco, []},
{mod_stats, []},
{mod_vcard, []},
{mod_offline, []},
{mod_announce, [{access, announce}]},
{mod_announce, [{access, announce}]}, % Depends on mod_adhoc
{mod_echo, [{host, "echo.localhost"}]},
{mod_private, []},
{mod_irc, []},
@@ -152,6 +166,8 @@
{mod_muc, [{access, muc},
{access_create, muc},
{access_admin, muc_admin}]},
% {mod_muc_log, []},
% {mod_shared_roster, []},
{mod_pubsub, []},
{mod_time, []},
{mod_last, []},
+1 -1
View File
@@ -6,7 +6,7 @@
%%% Id : $Id$
%%%----------------------------------------------------------------------
-define(VERSION, "1.0.0").
-define(VERSION, "1.1.1").
%-define(ejabberd_debug, true).
%-define(DBGFSM, true).
+1
View File
@@ -24,6 +24,7 @@ start(normal, _Args) ->
catch ssl:start(),
translate:start(),
acl:start(),
ejabberd_ctl:init(),
gen_mod:start(),
ejabberd_config:start(),
Sup = ejabberd_sup:start_link(),
+101 -31
View File
@@ -3,9 +3,13 @@
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Authentification
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
%%% Updated : 23 Feb 2006 by Mickael Remond <mremond@process-one.net>
%%% for anonymous login support
%%% Id : $Id$
%%%----------------------------------------------------------------------
%% TODO: Use the functions in ejabberd auth to add and remove users.
-module(ejabberd_auth).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
@@ -23,74 +27,140 @@
is_user_exists/2,
remove_user/2,
remove_user/3,
plain_password_required/1
plain_password_required/1,
ctl_process_get_registered/3
]).
-export([auth_modules/1]).
-include("ejabberd.hrl").
-include("ejabberd_ctl.hrl").
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start() ->
lists:foreach(fun(Host) ->
(auth_module(Host)):start(Host)
end, ?MYHOSTS).
lists:foreach(
fun(Host) ->
lists:foreach(
fun(M) ->
M:start(Host)
end, auth_modules(Host))
end, ?MYHOSTS).
plain_password_required(Server) ->
(auth_module(Server)):plain_password_required().
lists:any(
fun(M) ->
M:plain_password_required()
end, auth_modules(Server)).
check_password(User, Server, Password) ->
(auth_module(Server)):check_password(User, Server, Password).
lists:any(
fun(M) ->
M:check_password(User, Server, Password)
end, auth_modules(Server)).
check_password(User, Server, Password, StreamID, Digest) ->
(auth_module(Server)):check_password(User, Server, Password, StreamID, Digest).
lists:any(
fun(M) ->
M:check_password(User, Server, Password, StreamID, Digest)
end, auth_modules(Server)).
set_password(User, Server, Password) ->
(auth_module(Server)):set_password(User, Server, Password).
lists:foldl(
fun(M, {error, _}) ->
M:set_password(User, Server, Password);
(_M, Res) ->
Res
end, {error, not_allowed}, auth_modules(Server)).
try_register(User, Server, Password) ->
case lists:member(jlib:nameprep(Server), ?MYHOSTS) of
case is_user_exists(User,Server) of
true ->
(auth_module(Server)):try_register(User, Server, Password);
{atomic, exists};
false ->
{error, not_allowed}
case lists:member(jlib:nameprep(Server), ?MYHOSTS) of
true ->
lists:foldl(
fun(_M, {atomic, ok} = Res) ->
Res;
(M, _) ->
M:try_register(User, Server, Password)
end, {error, not_allowed}, auth_modules(Server));
false ->
{error, not_allowed}
end
end.
%% Registered users list do not include anonymous users logged
dirty_get_registered_users() ->
(auth_module(?MYNAME)):dirty_get_registered_users().
lists:flatmap(
fun(M) ->
M:dirty_get_registered_users()
end, auth_modules(?MYNAME)).
%% Registered users list do not include anonymous users logged
get_vh_registered_users(Server) ->
(auth_module(Server)):get_vh_registered_users(Server).
lists:flatmap(
fun(M) ->
M:get_vh_registered_users(Server)
end, auth_modules(Server)).
get_password(User, Server) ->
(auth_module(Server)):get_password(User, Server).
lists:foldl(
fun(M, false) ->
M:get_password(User, Server);
(_M, Password) ->
Password
end, false, auth_modules(Server)).
get_password_s(User, Server) ->
(auth_module(Server)):get_password_s(User, Server).
case get_password(User, Server) of
false ->
"";
Password ->
Password
end.
%% Returns true if the user exists in the DB or if an anonymous user is logged
%% under the given name
is_user_exists(User, Server) ->
(auth_module(Server)):is_user_exists(User, Server).
lists:any(
fun(M) ->
M:is_user_exists(User, Server)
end, auth_modules(Server)).
remove_user(User, Server) ->
(auth_module(Server)):remove_user(User, Server).
lists:foreach(
fun(M) ->
M:remove_user(User, Server)
end, auth_modules(Server)).
remove_user(User, Server, Password) ->
(auth_module(Server)):remove_user(User, Server, Password).
lists:foreach(
fun(M) ->
M:remove_user(User, Server, Password)
end, auth_modules(Server)).
ctl_process_get_registered(_Val, Host, ["registered-users"]) ->
Users = ejabberd_auth:get_vh_registered_users(Host),
NewLine = io_lib:format("~n", []),
SUsers = lists:sort(Users),
FUsers = lists:map(fun({U, _S}) -> [U, NewLine] end, SUsers),
io:format("~s", [FUsers]),
{stop, ?STATUS_SUCCESS};
ctl_process_get_registered(Val, _Host, _Args) ->
Val.
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
auth_module(Server) ->
auth_modules(Server) ->
LServer = jlib:nameprep(Server),
case ejabberd_config:get_local_option({auth_method, LServer}) of
external ->
ejabberd_auth_external;
ldap ->
ejabberd_auth_ldap;
odbc ->
ejabberd_auth_odbc;
_ ->
ejabberd_auth_internal
end.
Method = ejabberd_config:get_local_option({auth_method, LServer}),
Methods = if
is_list(Method) -> Method;
is_atom(Method) -> [Method]
end,
[list_to_atom("ejabberd_auth_" ++ atom_to_list(M)) || M <- Methods].
+220
View File
@@ -0,0 +1,220 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth_anonymous.erl
%%% Author : Mickael Remond <mickael.remond@process-one.net>
%%% Purpose : Anonymous feature support in ejabberd
%%% Created : 17 Feb 2006 by Mickael Remond <mremond@process-one.net>
%%%
%%% Anonymous support is based on the work of Magnus Henoch
%%% <henoch@dtek.chalmers.se> and heavily extended by Process-one.
%%%
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_auth_anonymous).
-author('mickael.remond@process-one.net').
-vsn('$Revision$ ').
-export([start/1,
allow_anonymous/1,
is_sasl_anonymous_enabled/1,
is_login_anonymous_enabled/1,
anonymous_user_exist/2,
allow_multiple_connections/1,
register_connection/2,
unregister_connection/2
]).
%% Function used by ejabberd_auth:
-export([login/2,
set_password/3,
check_password/3,
check_password/5,
try_register/3,
dirty_get_registered_users/0,
get_vh_registered_users/1,
get_password/2,
get_password/3,
is_user_exists/2,
remove_user/2,
remove_user/3,
plain_password_required/0]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(anonymous, {us, sid}).
%% Create the anonymous table if at least one virtual host has anonymous features enabled
%% Register to login / logout events
start(Host) ->
%% TODO: Check cluster mode
mnesia:create_table(anonymous, [{ram_copies, [node()]},
{type, bag},
{attributes, record_info(fields, anonymous)}]),
%% The hooks are needed to add / remove users from the anonymous tables
ejabberd_hooks:add(sm_register_connection_hook, Host,
?MODULE, register_connection, 100),
ejabberd_hooks:add(sm_remove_connection_hook, Host,
?MODULE, unregister_connection, 100),
ok.
%% Return true if anonymous is allowed for host or false otherwise
allow_anonymous(Host) ->
lists:member(?MODULE, ejabberd_auth:auth_modules(Host)).
%% Return true if anonymous mode is enabled and if anonymous protocol is SASL
%% anonymous protocol can be: sasl_anon|login_anon|both
is_sasl_anonymous_enabled(Host) ->
case allow_anonymous(Host) of
false -> false;
true ->
case anonymous_protocol(Host) of
sasl_anon -> true;
both -> true;
_Other -> false
end
end.
%% Return true if anonymous login is enabled on the server
%% anonymous login can be use using standard authentication method (i.e. with
%% clients that do not support anonymous login)
is_login_anonymous_enabled(Host) ->
case allow_anonymous(Host) of
false -> false;
true ->
case anonymous_protocol(Host) of
login_anon -> true;
both -> true;
_Other -> false
end
end.
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
%% defaults to login_anon
anonymous_protocol(Host) ->
case ejabberd_config:get_local_option({anonymous_protocol, Host}) of
sasl_anon -> sasl_anon;
login_anon -> login_anon;
both -> both;
_Other -> sasl_anon
end.
%% Return true if multiple connections have been allowed in the config file
%% defaults to false
allow_multiple_connections(Host) ->
case ejabberd_config:get_local_option({allow_multiple_connections, Host}) of
true -> true;
_Other -> false
end.
%% Check if user exist in the anonymus database
anonymous_user_exist(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({anonymous, US}) of
[] ->
false;
[_H|_T] ->
true
end.
%% Remove connection from Mnesia tables
remove_connection(SID, LUser, LServer) ->
US = {LUser, LServer},
F = fun() ->
mnesia:delete_object({anonymous, US, SID})
end,
mnesia:transaction(F).
%% Register connection
register_connection(SID, #jid{luser = LUser, lserver = LServer}) ->
US = {LUser, LServer},
mnesia:sync_dirty(
fun() -> mnesia:write(#anonymous{us = US, sid=SID})
end).
%% Remove an anonymous user from the anonymous users table
unregister_connection(SID, #jid{luser = LUser, lserver = LServer}) ->
remove_connection(SID, LUser, LServer).
%% ---------------------------------
%% Specific anonymous auth functions
%% ---------------------------------
%% When anonymous login is enabled, check the password for permenant users
%% before allowing access
check_password(User, Server, Password) ->
check_password(User, Server, Password, undefined, undefined).
check_password(User, Server, _Password, _StreamID, _Digest) ->
%% We refuse login for registered accounts (They cannot logged but
%% they however are "reserved")
case ejabberd_auth:is_user_exists(User, Server) of
true -> false;
false -> login(User, Server)
end.
login(User, Server) ->
case is_login_anonymous_enabled(Server) of
false -> false;
true ->
case anonymous_user_exist(User, Server) of
%% Reject the login if an anonymous user with the same login
%% is already logged and if multiple login has not been enable
%% in the config file.
true -> allow_multiple_connections(Server);
%% Accept login and add user to the anonymous table
false -> true
end
end.
%% When anonymous login is enabled, check that the user is permanent before
%% changing its password
set_password(User, Server, _Password) ->
case anonymous_user_exist(User, Server) of
true ->
ok;
false ->
{error, not_allowed}
end.
%% When anonymous login is enabled, check if permanent users are allowed on
%% the server:
try_register(_User, _Server, _Password) ->
{error, not_allowed}.
dirty_get_registered_users() ->
[].
get_vh_registered_users(_Server) ->
[].
%% Return password of permanent user or false for anonymous users
get_password(User, Server) ->
get_password(User, Server, "").
get_password(User, Server, DefaultValue) ->
case anonymous_user_exist(User, Server) of
%% We return the default value if the user is anonymous
true ->
DefaultValue;
%% We return the permanent user password otherwise
false ->
false
end.
%% Returns true if the user exists in the DB or if an anonymous user is logged
%% under the given name
is_user_exists(User, Server) ->
anonymous_user_exist(User, Server).
remove_user(_User, _Server) ->
{error, not_allowed}.
remove_user(_User, _Server, _Password) ->
not_allowed.
plain_password_required() ->
false.
+5 -1
View File
@@ -33,10 +33,14 @@
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start(_Host) ->
start(Host) ->
mnesia:create_table(passwd, [{disc_copies, [node()]},
{attributes, record_info(fields, passwd)}]),
update_table(),
ejabberd_ctl:register_commands(
Host,
[{"registered-users", "list all registered users"}],
ejabberd_auth, ctl_process_get_registered),
ok.
plain_password_required() ->
+4
View File
@@ -40,6 +40,10 @@ start(Host) ->
LDAPServers, 389, RootDN, Password),
eldap:start_link(get_eldap_id(Host, ejabberd_bind),
LDAPServers, 389, RootDN, Password),
ejabberd_ctl:register_commands(
Host,
[{"registered-users", "list all registered users"}],
ejabberd_auth, ctl_process_get_registered),
ok.
plain_password_required() ->
+46 -31
View File
@@ -34,7 +34,18 @@
%%% API
%%%----------------------------------------------------------------------
start(Host) ->
ejabberd_odbc_sup:start_link(Host),
ChildSpec =
{gen_mod:get_module_proc(Host, ejabberd_odbc_sup),
{ejabberd_odbc_sup, start_link, [Host]},
temporary,
infinity,
supervisor,
[ejabberd_odbc_sup]},
supervisor:start_child(ejabberd_sup, ChildSpec),
ejabberd_ctl:register_commands(
Host,
[{"registered-users", "list all registered users"}],
ejabberd_auth, ctl_process_get_registered),
ok.
plain_password_required() ->
@@ -49,7 +60,7 @@ check_password(User, Server, Password) ->
case catch ejabberd_odbc:sql_query(
jlib:nameprep(Server),
["select password from users "
"where username='", Username, "'"]) of
"where username='", Username, "';"]) of
{selected, ["password"], [{Password}]} ->
true;
_ ->
@@ -66,7 +77,7 @@ check_password(User, Server, Password, StreamID, Digest) ->
case catch ejabberd_odbc:sql_query(
jlib:nameprep(Server),
["select password from users "
"where username='", Username, "'"]) of
"where username='", Username, "';"]) of
{selected, ["password"], [{Passwd}]} ->
DigRes = if
Digest /= "" ->
@@ -91,12 +102,12 @@ set_password(User, Server, Password) ->
LUser ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
catch ejabberd_odbc:sql_query(
jlib:nameprep(Server),
["begin;"
"delete from users where username='", Username ,"';"
"insert into users(username, password) "
"values ('", Username, "', '", Pass, "'); commit"])
LServer = jlib:nameprep(Server),
catch ejabberd_odbc:sql_transaction(
LServer,
[["delete from users where username='", Username ,"';"],
["insert into users(username, password) "
"values ('", Username, "', '", Pass, "');"]])
end.
@@ -110,7 +121,7 @@ try_register(User, Server, Password) ->
case catch ejabberd_odbc:sql_query(
jlib:nameprep(Server),
["insert into users(username, password) "
"values ('", Username, "', '", Pass, "')"]) of
"values ('", Username, "', '", Pass, "');"]) of
{updated, 1} ->
{atomic, ok};
_ ->
@@ -141,7 +152,7 @@ get_password(User, Server) ->
case catch ejabberd_odbc:sql_query(
jlib:nameprep(Server),
["select password from users "
"where username='", Username, "'"]) of
"where username='", Username, "';"]) of
{selected, ["password"], [{Password}]} ->
Password;
_ ->
@@ -158,7 +169,7 @@ get_password_s(User, Server) ->
case catch ejabberd_odbc:sql_query(
jlib:nameprep(Server),
["select password from users "
"where username='", Username, "'"]) of
"where username='", Username, "';"]) of
{selected, ["password"], [{Password}]} ->
Password;
_ ->
@@ -175,7 +186,7 @@ is_user_exists(User, Server) ->
case catch ejabberd_odbc:sql_query(
jlib:nameprep(Server),
["select password from users "
"where username='", Username, "'"]) of
"where username='", Username, "';"]) of
{selected, ["password"], [{_Password}]} ->
true;
_ ->
@@ -191,7 +202,7 @@ remove_user(User, Server) ->
Username = ejabberd_odbc:escape(LUser),
catch ejabberd_odbc:sql_query(
jlib:nameprep(Server),
["delete from users where username='", Username ,"'"]),
["delete from users where username='", Username ,"';"]),
ejabberd_hooks:run(remove_user, jlib:nameprep(Server),
[User, Server])
end.
@@ -203,21 +214,25 @@ remove_user(User, Server, Password) ->
LUser ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
case catch
ejabberd_odbc:sql_query(
jlib:nameprep(Server),
["begin;"
"select password from users where username='", Username, "';"
"delete from users "
"where username='", Username, "' and password='", Pass, "';"
"commit"]) of
{selected, ["password"], [{Password}]} ->
ejabberd_hooks:run(remove_user, jlib:nameprep(Server),
[User, Server]),
ok;
{selected, ["password"], []} ->
not_exists;
_ ->
not_allowed
end
LServer = jlib:nameprep(Server),
F = fun() ->
Result = ejabberd_odbc:sql_query_t(
["select password from users where username='",
Username, "';"]),
ejabberd_odbc:sql_query_t(["delete from users "
"where username='", Username,
"' and password='", Pass, "';"]),
case Result of
{selected, ["password"], [{Password}]} ->
ejabberd_hooks:run(remove_user, jlib:nameprep(Server),
[User, Server]),
ok;
{selected, ["password"], []} ->
not_exists;
_ ->
not_allowed
end
end,
{atomic, Result } = ejabberd_odbc:transaction(LServer, F),
Result
end.
+263 -70
View File
@@ -8,7 +8,7 @@
-module(ejabberd_c2s).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-update_info({update, 0}).
-behaviour(gen_fsm).
@@ -17,6 +17,7 @@
start_link/2,
send_text/2,
send_element/2,
become_controller/1,
get_presence/1]).
%% gen_fsm callbacks
@@ -45,6 +46,7 @@
sasl_state,
access,
shaper,
zlib = false,
tls = false,
tls_required = false,
tls_enabled = false,
@@ -52,6 +54,7 @@
authenticated = false,
jid,
user = "", server = ?MYNAME, resource = "",
sid,
pres_t = ?SETS:new(),
pres_f = ?SETS:new(),
pres_a = ?SETS:new(),
@@ -97,6 +100,9 @@ start(SockData, Opts) ->
start_link(SockData, Opts) ->
gen_fsm:start_link(ejabberd_c2s, [SockData, Opts], ?FSMOPTS).
become_controller(Pid) ->
gen_fsm:send_all_state_event(Pid, become_controller).
%% Return Username, Resource and presence information
get_presence(FsmRef) ->
gen_fsm:sync_send_all_state_event(FsmRef, {get_presence}, 1000).
@@ -121,6 +127,12 @@ init([{SockMod, Socket}, Opts]) ->
{value, {_, S}} -> S;
_ -> none
end,
MaxStanzaSize =
case lists:keysearch(max_stanza_size, 1, Opts) of
{value, {_, Size}} -> Size;
_ -> infinity
end,
Zlib = lists:member(zlib, Opts),
StartTLS = lists:member(starttls, Opts),
StartTLSRequired = lists:member(starttls_required, Opts),
TLSEnabled = lists:member(tls, Opts),
@@ -132,15 +144,18 @@ init([{SockMod, Socket}, Opts]) ->
if
TLSEnabled ->
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
RecPid = ejabberd_receiver:start(TLSSocket, tls, none),
RecPid = ejabberd_receiver:start(
TLSSocket, tls, none, MaxStanzaSize),
{tls, TLSSocket, RecPid};
true ->
RecPid = ejabberd_receiver:start(Socket, SockMod, none),
RecPid = ejabberd_receiver:start(
Socket, SockMod, none, MaxStanzaSize),
{SockMod, Socket, RecPid}
end,
{ok, wait_for_stream, #state{socket = Socket1,
sockmod = SockMod1,
receiver = ReceiverPid,
zlib = Zlib,
tls = TLS,
tls_required = StartTLSRequired,
tls_enabled = TLSEnabled,
@@ -196,10 +211,22 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
{xmlelement, "mechanism", [],
[{xmlcdata, S}]}
end, cyrsasl:listmech(Server)),
SockMod = StateData#state.sockmod,
Zlib = StateData#state.zlib,
CompressFeature =
case Zlib andalso
(SockMod == gen_tcp) of
true ->
[{xmlelement, "compression",
[{"xmlns", ?NS_FEATURE_COMPRESS}],
[{xmlelement, "method",
[], [{xmlcdata, "zlib"}]}]}];
_ ->
[]
end,
TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled,
TLSRequired = StateData#state.tls_required,
SockMod = StateData#state.sockmod,
TLSFeature =
case (TLS == true) andalso
(TLSEnabled == false) andalso
@@ -220,7 +247,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
end,
send_element(StateData,
{xmlelement, "stream:features", [],
TLSFeature ++
TLSFeature ++ CompressFeature ++
[{xmlelement, "mechanisms",
[{"xmlns", ?NS_SASL}],
Mechs}] ++
@@ -307,18 +334,20 @@ wait_for_stream(closed, StateData) ->
wait_for_auth({xmlstreamelement, El}, StateData) ->
case is_auth_packet(El) of
{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),
case U of
"" ->
UCdata = [];
_ ->
UCdata = [{xmlcdata, U}]
end,
Res = case ejabberd_auth:plain_password_required(
StateData#state.server) of
false ->
{xmlelement, Name, Attrs,
[{xmlelement, "query", [{"xmlns", ?NS_AUTH}],
[{xmlelement, "username", [], [{xmlcdata, U}]},
[{xmlelement, "username", [], UCdata},
{xmlelement, "password", [], []},
{xmlelement, "digest", [], []},
{xmlelement, "resource", [], []}
@@ -326,14 +355,14 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
true ->
{xmlelement, Name, Attrs,
[{xmlelement, "query", [{"xmlns", ?NS_AUTH}],
[{xmlelement, "username", [], [{xmlcdata, U}]},
[{xmlelement, "username", [], UCdata},
{xmlelement, "password", [], []},
{xmlelement, "resource", [], []}
]}]}
end,
send_element(StateData, Res),
{next_state, wait_for_auth, StateData};
{auth, _ID, set, {U, P, D, ""}} ->
{auth, _ID, set, {_U, _P, _D, ""}} ->
Err = jlib:make_error_reply(
El,
?ERR_AUTH_NO_RESOURCE_PROVIDED(StateData#state.lang)),
@@ -353,8 +382,9 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
"(~w) Accepted legacy authentication for ~s",
[StateData#state.socket,
jlib:jid_to_string(JID)]),
SID = {now(), self()},
ejabberd_sm:open_session(
U, StateData#state.server, R),
SID, U, StateData#state.server, R),
Res1 = jlib:make_result_iq_reply(El),
Res = setelement(4, Res1, []),
send_element(StateData, Res),
@@ -378,6 +408,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
StateData#state{user = U,
resource = R,
jid = JID,
sid = SID,
pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList}};
@@ -430,6 +461,7 @@ wait_for_auth(closed, StateData) ->
wait_for_feature_request({xmlstreamelement, El}, StateData) ->
{xmlelement, Name, Attrs, Els} = El,
Zlib = StateData#state.zlib,
TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled,
TLSRequired = StateData#state.tls_required,
@@ -482,7 +514,6 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
lists:keydelete(
certfile, 1, StateData#state.tls_options)]
end,
io:format("O: ~p~n", [TLSOpts]),
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
ejabberd_receiver:starttls(StateData#state.receiver, TLSSocket),
send_element(StateData,
@@ -493,6 +524,40 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
streamid = new_id(),
tls_enabled = true
}};
{?NS_COMPRESS, "compress"} when Zlib == true,
SockMod == gen_tcp ->
case xml:get_subtag(El, "method") of
false ->
send_element(StateData,
{xmlelement, "failure",
[{"xmlns", ?NS_COMPRESS}],
[{xmlelement, "setup-failed", [], []}]}),
{next_state, wait_for_feature_request, StateData};
Method ->
case xml:get_tag_cdata(Method) of
"zlib" ->
Socket = StateData#state.socket,
{ok, ZlibSocket} = ejabberd_zlib:enable_zlib(SockMod,
Socket),
ejabberd_receiver:compress(StateData#state.receiver,
ZlibSocket),
send_element(StateData,
{xmlelement, "compressed",
[{"xmlns", ?NS_COMPRESS}], []}),
{next_state, wait_for_stream,
StateData#state{sockmod = ejabberd_zlib,
socket = ZlibSocket,
streamid = new_id()
}};
_ ->
send_element(StateData,
{xmlelement, "failure",
[{"xmlns", ?NS_COMPRESS}],
[{xmlelement, "unsupported-method",
[], []}]}),
{next_state, wait_for_feature_request, StateData}
end
end;
_ ->
if
(SockMod == gen_tcp) and TLSRequired ->
@@ -631,8 +696,9 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
?INFO_MSG("(~w) Opened session for ~s",
[StateData#state.socket,
jlib:jid_to_string(JID)]),
SID = {now(), self()},
ejabberd_sm:open_session(
U, StateData#state.server, R),
SID, U, StateData#state.server, R),
Res = jlib:make_result_iq_reply(El),
send_element(StateData, Res),
change_shaper(StateData, JID),
@@ -651,7 +717,8 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
PL -> PL
end,
{next_state, session_established,
StateData#state{pres_f = ?SETS:from_list(Fs1),
StateData#state{sid = SID,
pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList}};
_ ->
@@ -693,15 +760,16 @@ session_established({xmlstreamelement, El}, StateData) ->
_ ->
jlib:string_to_jid(To)
end,
NewEl1 = jlib:remove_attr("xmlns", El),
NewEl = case xml:get_attr_s("xml:lang", Attrs) of
"" ->
case StateData#state.lang of
"" -> El;
"" -> NewEl1;
Lang ->
xml:replace_tag_attr("xml:lang", Lang, El)
xml:replace_tag_attr("xml:lang", Lang, NewEl1)
end;
_ ->
El
NewEl1
end,
NewState =
case ToJID of
@@ -791,6 +859,12 @@ session_established(closed, StateData) ->
%% {next_state, NextStateName, NextStateData, Timeout} |
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
handle_event(become_controller, StateName, StateData) ->
ok = (StateData#state.sockmod):controlling_process(
StateData#state.socket,
StateData#state.receiver),
ejabberd_receiver:become_controller(StateData#state.receiver),
{next_state, StateName, StateData};
handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
@@ -845,8 +919,38 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
"presence" ->
case xml:get_attr_s("type", Attrs) of
"probe" ->
process_presence_probe(From, To, StateData),
{false, Attrs, StateData};
LFrom = jlib:jid_tolower(From),
LBFrom = jlib:jid_remove_resource(LFrom),
NewStateData =
case ?SETS:is_element(
LFrom, StateData#state.pres_a) orelse
?SETS:is_element(
LBFrom, StateData#state.pres_a) of
true ->
StateData;
false ->
case ?SETS:is_element(
LFrom, StateData#state.pres_f) of
true ->
A = ?SETS:add_element(
LFrom,
StateData#state.pres_a),
StateData#state{pres_a = A};
false ->
case ?SETS:is_element(
LBFrom, StateData#state.pres_f) of
true ->
A = ?SETS:add_element(
LBFrom,
StateData#state.pres_a),
StateData#state{pres_a = A};
false ->
StateData
end
end
end,
process_presence_probe(From, To, NewStateData),
{false, Attrs, NewStateData};
"error" ->
NewA = remove_element(jlib:jid_tolower(From),
StateData#state.pres_a),
@@ -873,7 +977,37 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
{'EXIT', _Reason} ->
{true, Attrs, StateData};
allow ->
{true, Attrs, StateData};
LFrom = jlib:jid_tolower(From),
LBFrom = jlib:jid_remove_resource(LFrom),
case ?SETS:is_element(
LFrom, StateData#state.pres_a) orelse
?SETS:is_element(
LBFrom, StateData#state.pres_a) of
true ->
{true, Attrs, StateData};
false ->
case ?SETS:is_element(
LFrom, StateData#state.pres_f) of
true ->
A = ?SETS:add_element(
LFrom,
StateData#state.pres_a),
{true, Attrs,
StateData#state{pres_a = A}};
false ->
case ?SETS:is_element(
LBFrom, StateData#state.pres_f) of
true ->
A = ?SETS:add_element(
LBFrom,
StateData#state.pres_a),
{true, Attrs,
StateData#state{pres_a = A}};
false ->
{true, Attrs, StateData}
end
end
end;
deny ->
{false, Attrs, StateData}
end
@@ -890,16 +1024,30 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
StateData)};
[{exit, Reason}] ->
{exit, Attrs, Reason};
[{privacy_list, PrivList}] ->
{false, Attrs,
[{privacy_list, PrivList, PrivListName}] ->
case catch mod_privacy:updated_list(
StateData#state.privacy_list,
PrivList) of
{'EXIT', _} ->
{false, Attrs, StateData};
NewPL ->
StateData#state{privacy_list = NewPL}
end};
PrivPushIQ =
#iq{type = set, xmlns = ?NS_PRIVACY,
id = "push",
sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_PRIVACY}],
[{xmlelement, "list",
[{"name", PrivListName}],
[]}]}]},
PrivPushEl =
jlib:replace_from_to(
jlib:jid_remove_resource(
StateData#state.jid),
StateData#state.jid,
jlib:iq_to_xml(PrivPushIQ)),
send_element(StateData, PrivPushEl),
{false, Attrs, StateData#state{privacy_list = NewPL}}
end;
_ ->
{false, Attrs, StateData}
end;
@@ -975,7 +1123,10 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
{next_state, StateName, NewState};
true ->
{next_state, StateName, NewState}
end.
end;
handle_info(Info, StateName, StateData) ->
?ERROR_MSG("Unexpected info: ~p", [Info]),
{next_state, StateName, StateData}.
%%----------------------------------------------------------------------
%% Func: terminate/3
@@ -995,10 +1146,12 @@ terminate(_Reason, StateName, StateData) ->
[{"type", "unavailable"}],
[{xmlelement, "status", [],
[{xmlcdata, "Replaced by new connection"}]}]},
ejabberd_sm:unset_presence(StateData#state.user,
StateData#state.server,
StateData#state.resource,
"Replaced by new connection"),
ejabberd_sm:close_session_unset_presence(
StateData#state.sid,
StateData#state.user,
StateData#state.server,
StateData#state.resource,
"Replaced by new connection"),
presence_broadcast(
StateData, From, StateData#state.pres_a, Packet),
presence_broadcast(
@@ -1007,25 +1160,27 @@ terminate(_Reason, StateName, StateData) ->
?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.server,
StateData#state.resource),
Tmp = ?SETS:new(),
EmptySet = ?SETS:new(),
case StateData of
#state{pres_last = undefined,
pres_a = Tmp,
pres_i = Tmp,
pres_a = EmptySet,
pres_i = EmptySet,
pres_invis = false} ->
ok;
ejabberd_sm:close_session(StateData#state.sid,
StateData#state.user,
StateData#state.server,
StateData#state.resource);
_ ->
From = StateData#state.jid,
Packet = {xmlelement, "presence",
[{"type", "unavailable"}], []},
ejabberd_sm:unset_presence(StateData#state.user,
StateData#state.server,
StateData#state.resource,
""),
ejabberd_sm:close_session_unset_presence(
StateData#state.sid,
StateData#state.user,
StateData#state.server,
StateData#state.resource,
""),
presence_broadcast(
StateData, From, StateData#state.pres_a, Packet),
presence_broadcast(
@@ -1035,7 +1190,7 @@ terminate(_Reason, StateName, StateData) ->
_ ->
ok
end,
(StateData#state.sockmod):close(StateData#state.socket),
ejabberd_receiver:close(StateData#state.receiver),
ok.
%%%----------------------------------------------------------------------
@@ -1048,7 +1203,8 @@ change_shaper(StateData, JID) ->
ejabberd_receiver:change_shaper(StateData#state.receiver, Shaper).
send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket, Text).
?DEBUG("Send XML on stream = ~p", [lists:flatten(Text)]),
catch (StateData#state.sockmod):send(StateData#state.socket, Text).
send_element(StateData, El) ->
send_text(StateData, xml:element_to_string(El)).
@@ -1147,7 +1303,8 @@ presence_update(From, Packet, StateData) ->
StatusTag ->
xml:get_tag_cdata(StatusTag)
end,
ejabberd_sm:unset_presence(StateData#state.user,
ejabberd_sm:unset_presence(StateData#state.sid,
StateData#state.user,
StateData#state.server,
StateData#state.resource,
Status),
@@ -1189,7 +1346,14 @@ presence_update(From, Packet, StateData) ->
"unsubscribed" ->
StateData;
_ ->
update_priority(xml:get_subtag(Packet, "priority"), StateData),
OldPriority = case StateData#state.pres_last of
undefined ->
0;
OldPresence ->
get_priority_from_presence(OldPresence)
end,
NewPriority = get_priority_from_presence(Packet),
update_priority(NewPriority, StateData),
FromUnavail = (StateData#state.pres_last == undefined) or
StateData#state.pres_invis,
?DEBUG("from unavail = ~p~n", [FromUnavail]),
@@ -1210,6 +1374,11 @@ presence_update(From, Packet, StateData) ->
StateData#state.pres_f,
StateData#state.pres_a,
Packet),
if OldPriority < 0, NewPriority >= 0 ->
resend_offline_messages(StateData);
true ->
ok
end,
StateData#state{pres_last = Packet,
pres_invis = false
}
@@ -1236,28 +1405,28 @@ presence_track(From, To, Packet, StateData) ->
StateData#state{pres_i = I,
pres_a = A};
"subscribe" ->
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
ejabberd_hooks:run(roster_out_subscription,
Server,
[User, Server, To, subscribe]),
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
StateData;
"subscribed" ->
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
ejabberd_hooks:run(roster_out_subscription,
Server,
[User, Server, To, subscribed]),
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
StateData;
"unsubscribe" ->
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
ejabberd_hooks:run(roster_out_subscription,
Server,
[User, Server, To, unsubscribe]),
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
StateData;
"unsubscribed" ->
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
ejabberd_hooks:run(roster_out_subscription,
Server,
[User, Server, To, unsubscribed]),
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
StateData;
"error" ->
ejabberd_router:route(From, To, Packet),
@@ -1389,6 +1558,7 @@ roster_change(IJID, ISubscription, StateData) ->
LIJID = jlib:jid_tolower(IJID),
IsFrom = (ISubscription == both) or (ISubscription == from),
IsTo = (ISubscription == both) or (ISubscription == to),
OldIsFrom = ?SETS:is_element(LIJID, StateData#state.pres_f),
FSet = if
IsFrom ->
?SETS:add_element(LIJID, StateData#state.pres_f);
@@ -1407,14 +1577,26 @@ roster_change(IJID, ISubscription, StateData) ->
P ->
?DEBUG("roster changed for ~p~n", [StateData#state.user]),
From = StateData#state.jid,
Cond1 = (not StateData#state.pres_invis) and IsFrom,
Cond2 = (not IsFrom)
To = jlib:make_jid(IJID),
Cond1 = (not StateData#state.pres_invis) and IsFrom
and (not OldIsFrom),
Cond2 = (not IsFrom) and OldIsFrom
and (?SETS:is_element(LIJID, StateData#state.pres_a) or
?SETS:is_element(LIJID, StateData#state.pres_i)),
if
Cond1 ->
?DEBUG("C1: ~p~n", [LIJID]),
ejabberd_router:route(From, jlib:make_jid(IJID), P),
case catch mod_privacy:check_packet(
StateData#state.user,
StateData#state.server,
StateData#state.privacy_list,
{From, To, P},
out) of
deny ->
ok;
_ ->
ejabberd_router:route(From, To, P)
end,
A = ?SETS:add_element(LIJID,
StateData#state.pres_a),
StateData#state{pres_a = A,
@@ -1422,9 +1604,19 @@ roster_change(IJID, ISubscription, StateData) ->
pres_t = TSet};
Cond2 ->
?DEBUG("C2: ~p~n", [LIJID]),
ejabberd_router:route(From, jlib:make_jid(IJID),
{xmlelement, "presence",
[{"type", "unavailable"}], []}),
PU = {xmlelement, "presence",
[{"type", "unavailable"}], []},
case catch mod_privacy:check_packet(
StateData#state.user,
StateData#state.server,
StateData#state.privacy_list,
{From, To, PU},
out) of
deny ->
ok;
_ ->
ejabberd_router:route(From, To, PU)
end,
I = remove_element(LIJID,
StateData#state.pres_i),
A = remove_element(LIJID,
@@ -1439,24 +1631,25 @@ roster_change(IJID, ISubscription, StateData) ->
end.
update_priority(El, StateData) ->
Pri = case El of
false ->
0;
_ ->
case catch list_to_integer(xml:get_tag_cdata(El)) of
P when is_integer(P) ->
P;
_ ->
0
end
end,
ejabberd_sm:set_presence(StateData#state.user,
update_priority(Pri, StateData) ->
ejabberd_sm:set_presence(StateData#state.sid,
StateData#state.user,
StateData#state.server,
StateData#state.resource,
Pri).
get_priority_from_presence(PresencePacket) ->
case xml:get_subtag(PresencePacket, "priority") of
false ->
0;
SubEl ->
case catch list_to_integer(xml:get_tag_cdata(SubEl)) of
P when is_integer(P) ->
P;
_ ->
0
end
end.
process_privacy_iq(From, To,
#iq{type = Type, sub_el = SubEl} = IQ,
+193 -163
View File
@@ -9,257 +9,287 @@
-module(ejabberd_ctl).
-author('alexey@sevcom.net').
-export([start/0, dump_to_textfile/1]).
-export([start/0,
init/0,
process/1,
register_commands/3,
register_commands/4,
unregister_commands/3,
unregister_commands/4]).
-define(STATUS_SUCCESS, 0).
-define(STATUS_ERROR, 1).
-define(STATUS_USAGE, 2).
-define(STATUS_BADRPC, 3).
-include("ejabberd_ctl.hrl").
start() ->
case init:get_plain_arguments() of
[SNode | Args] ->
Node = list_to_atom(SNode),
Status = process(Node, Args),
Status = case rpc:call(Node, ?MODULE, process, [Args]) of
{badrpc, Reason} ->
io:format("RPC failed on the node ~p: ~p~n",
[Node, Reason]),
?STATUS_BADRPC;
S ->
S
end,
halt(Status);
_ ->
print_usage(),
halt(?STATUS_USAGE)
end.
init() ->
ets:new(ejabberd_ctl_cmds, [named_table, set, public]),
ets:new(ejabberd_ctl_host_cmds, [named_table, set, public]).
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
process(["status"]) ->
{InternalStatus, ProvidedStatus} = init:get_status(),
io:format("Node ~p is ~p. Status: ~p~n",
[node(), InternalStatus, ProvidedStatus]),
case lists:keysearch(ejabberd, 1, application:which_applications()) of
false ->
io:format("ejabberd is not running~n", []),
?STATUS_ERROR;
{value,_Version} ->
io:format("ejabberd is running~n", []),
?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(["stop"]) ->
init:stop(),
?STATUS_SUCCESS;
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(["restart"]) ->
init:restart(),
?STATUS_SUCCESS;
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(["reopen-log"]) ->
ejabberd_logger_h:reopen_log(),
?STATUS_SUCCESS;
process(Node, ["register", User, Server, Password]) ->
case rpc:call(Node, ejabberd_auth, try_register, [User, Server, Password]) of
process(["register", User, Server, Password]) ->
case ejabberd_auth:try_register(User, Server, Password) of
{atomic, ok} ->
?STATUS_SUCCESS;
{atomic, exists} ->
io:format("User ~p already registered at node ~p~n",
[User ++ "@" ++ Server, Node]),
[User ++ "@" ++ Server, node()]),
?STATUS_ERROR;
{error, Reason} ->
io:format("Can't register user ~p at node ~p: ~p~n",
[User ++ "@" ++ Server, Node, Reason]),
?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't register user ~p at node ~p: ~p~n",
[User ++ "@" ++ Server, Node, Reason]),
?STATUS_BADRPC
[User ++ "@" ++ Server, node(), Reason]),
?STATUS_ERROR
end;
process(Node, ["unregister", User, Server]) ->
case rpc:call(Node, ejabberd_auth, remove_user, [User, Server]) of
process(["unregister", User, Server]) ->
case ejabberd_auth:remove_user(User, Server) of
{error, Reason} ->
io:format("Can't unregister user ~p at node ~p: ~p~n",
[User ++ "@" ++ Server, Node, Reason]),
[User ++ "@" ++ Server, node(), Reason]),
?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't unregister user ~p at node ~p: ~p~n",
[User ++ "@" ++ Server, Node, Reason]),
?STATUS_BADRPC;
_ ->
?STATUS_SUCCESS
end;
process(Node, ["backup", Path]) ->
case rpc:call(Node, mnesia, backup, [Path]) of
process(["backup", Path]) ->
case mnesia:backup(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't store backup in ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't store backup in ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_BADRPC
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(Node, ["dump", Path]) ->
case rpc:call(Node, ejabberd_ctl, dump_to_textfile, [Path]) of
process(["dump", Path]) ->
case dump_to_textfile(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't store dump in ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't store dump in ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_BADRPC
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(Node, ["load", Path]) ->
case rpc:call(Node, mnesia, load_textfile, [Path]) of
process(["load", Path]) ->
case mnesia:load_textfile(Path) of
{atomic, ok} ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't load dump in ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't load dump in ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_BADRPC
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(Node, ["restore", Path]) ->
case rpc:call(Node,
mnesia, restore, [Path, [{default_op, keep_tables}]]) of
process(["restore", Path]) ->
case mnesia:restore(Path, [{default_op, keep_tables}]) of
{atomic, _} ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't restore backup from ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't restore backup from ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_BADRPC
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(Node, ["install-fallback", Path]) ->
case rpc:call(Node, mnesia, install_fallback, [Path]) of
process(["install-fallback", Path]) ->
case mnesia:install_fallback(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't install fallback from ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't install fallback from ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_BADRPC
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(Node, ["import-file", Path]) ->
case rpc:call(Node, jd2ejd, import_file, [Path]) of
process(["import-file", Path]) ->
case jd2ejd:import_file(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't import jabberd 1.4 spool file ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't import jabberd 1.4 spool file ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_BADRPC
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(Node, ["import-dir", Path]) ->
case rpc:call(Node, jd2ejd, import_dir, [Path]) of
process(["import-dir", Path]) ->
case jd2ejd:import_dir(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't import jabberd 1.4 spool dir ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["delete-expired-messages"]) ->
mod_offline:remove_expired_messages(),
?STATUS_SUCCESS;
process(["vhost", H | Args]) ->
case jlib:nameprep(H) of
false ->
io:format("Bad hostname: ~p~n", [H]),
?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't import jabberd 1.4 spool dir ~p at node ~p: ~p~n",
[filename:absname(Path), Node, Reason]),
?STATUS_BADRPC
Host ->
case ejabberd_hooks:run_fold(
ejabberd_ctl_process, Host, false, [Host, Args]) of
false ->
print_vhost_usage(Host),
?STATUS_USAGE;
Status ->
Status
end
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, S}) -> [U, $@, S, NewLine] end, SUsers),
io:format("~s", [FUsers]),
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't get list of registered users at node ~p: ~p~n",
[Node, Reason]),
?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't get list of registered users at node ~p: ~p~n",
[Node, Reason]),
?STATUS_BADRPC
end;
process(Node, ["delete-expired-messages"]) ->
case rpc:call(Node, mod_offline, remove_expired_messages, []) of
{badrpc, Reason} ->
io:format("Can't delete expired messages at node ~p: ~p~n",
[Node, Reason]),
?STATUS_BADRPC;
_ ->
?STATUS_SUCCESS
end;
process(_Node, _Args) ->
print_usage(),
?STATUS_USAGE.
process(Args) ->
case ejabberd_hooks:run_fold(ejabberd_ctl_process, false, [Args]) of
false ->
print_usage(),
?STATUS_USAGE;
Status ->
Status
end.
print_usage() ->
CmdDescs =
[{"status", "get ejabberd status"},
{"stop", "stop ejabberd"},
{"restart", "restart ejabberd"},
{"reopen-log", "reopen log file"},
{"register user server password", "register a user"},
{"unregister user server", "unregister a user"},
{"backup file", "store a database backup to file"},
{"restore file", "restore a database backup from file"},
{"install-fallback file", "install a database fallback from file"},
{"dump file", "dump a database to a text file"},
{"load file", "restore a database from a text file"},
{"import-file file", "import user data from jabberd 1.4 spool file"},
{"import-dir dir", "import user data from jabberd 1.4 spool directory"},
{"delete-expired-messages", "delete expired offline messages from database"},
{"vhost host ...", "execute host-specific commands"}] ++
ets:tab2list(ejabberd_ctl_cmds),
MaxCmdLen =
lists:max(lists:map(
fun({Cmd, _Desc}) ->
length(Cmd)
end, CmdDescs)),
NewLine = io_lib:format("~n", []),
FmtCmdDescs =
lists:map(
fun({Cmd, Desc}) ->
[" ", Cmd, string:chars($\s, MaxCmdLen - length(Cmd) + 2),
Desc, NewLine]
end, CmdDescs),
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 server password\tregister a user~n"
" unregister user server\tunregister a user~n"
" backup file\t\t\tstore a database backup to file~n"
" restore file\t\t\trestore a database backup from file~n"
" install-fallback file\t\tinstall a database fallback from file~n"
" dump file\t\t\tdump a database to a text file~n"
" load file\t\t\trestore a database from a text file~n"
" import-file file\t\timport user data from jabberd 1.4 spool file~n"
" import-dir dir\t\timport user data from jabberd 1.4 spool directory~n"
" registered-users\t\tlist all registered users~n"
" delete-expired-messages\tdelete expired offline messages from database~n"
++ FmtCmdDescs ++
"~n"
"Example:~n"
" ejabberdctl ejabberd@host restart~n"
).
print_vhost_usage(Host) ->
CmdDescs =
ets:select(ejabberd_ctl_host_cmds,
[{{Host, '$1', '$2'}, [], [{{'$1', '$2'}}]}]),
MaxCmdLen =
if
CmdDescs == [] ->
0;
true ->
lists:max(lists:map(
fun({Cmd, _Desc}) ->
length(Cmd)
end, CmdDescs))
end,
NewLine = io_lib:format("~n", []),
FmtCmdDescs =
lists:map(
fun({Cmd, Desc}) ->
[" ", Cmd, string:chars($\s, MaxCmdLen - length(Cmd) + 2),
Desc, NewLine]
end, CmdDescs),
io:format(
"Usage: ejabberdctl node vhost host command~n"
"~n"
"Available commands:~n"
++ FmtCmdDescs ++
"~n"
).
register_commands(CmdDescs, Module, Function) ->
ets:insert(ejabberd_ctl_cmds, CmdDescs),
ejabberd_hooks:add(ejabberd_ctl_process,
Module, Function, 50),
ok.
register_commands(Host, CmdDescs, Module, Function) ->
ets:insert(ejabberd_ctl_host_cmds,
[{Host, Cmd, Desc} || {Cmd, Desc} <- CmdDescs]),
ejabberd_hooks:add(ejabberd_ctl_process, Host,
Module, Function, 50),
ok.
unregister_commands(CmdDescs, Module, Function) ->
lists:foreach(fun(CmdDesc) ->
ets:delete_object(ejabberd_ctl_cmds, CmdDesc)
end, CmdDescs),
ejabberd_hooks:delete(ejabberd_ctl_process,
Module, Function, 50),
ok.
unregister_commands(Host, CmdDescs, Module, Function) ->
lists:foreach(fun({Cmd, Desc}) ->
ets:delete_object(ejabberd_ctl_host_cmds,
{Host, Cmd, Desc})
end, CmdDescs),
ejabberd_hooks:delete(ejabberd_ctl_process,
Module, Function, 50),
ok.
dump_to_textfile(File) ->
dump_to_textfile(mnesia:system_info(is_running), file:open(File, write)).
dump_to_textfile(yes, {ok, F}) ->
@@ -286,7 +316,7 @@ dump_to_textfile(_, {ok, F}) ->
dump_to_textfile(_, {error, Reason}) ->
{error, Reason}.
dump_tab(F, T) ->
W = mnesia:table_info(T, wild_pattern),
{atomic,All} = mnesia:transaction(
+12
View File
@@ -0,0 +1,12 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_ctl.hrl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose :
%%% Created : 13 Feb 2006 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-define(STATUS_SUCCESS, 0).
-define(STATUS_ERROR, 1).
-define(STATUS_USAGE, 2).
-define(STATUS_BADRPC, 3).
+2
View File
@@ -99,6 +99,7 @@ accept(ListenSocket, Module, Opts) ->
{error, _Reason} ->
gen_tcp:close(Socket)
end,
Module:become_controller(Pid),
accept(ListenSocket, Module, Opts);
{error, Reason} ->
?INFO_MSG("(~w) Failed TCP accept: ~w",
@@ -146,6 +147,7 @@ accept_ssl(ListenSocket, Module, Opts) ->
end,
{ok, Pid} = Module:start({ssl, Socket}, Opts),
catch ssl:controlling_process(Socket, Pid),
Module:become_controller(Pid),
accept_ssl(ListenSocket, Module, Opts);
{error, timeout} ->
accept_ssl(ListenSocket, Module, Opts);
+161 -91
View File
@@ -10,7 +10,10 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-export([start_link/0, init/0]).
-behaviour(gen_server).
%% API
-export([start_link/0]).
-export([route/3,
register_iq_handler/4,
@@ -20,102 +23,26 @@
bounce_resource_packet/3
]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(state, {}).
-define(IQTABLE, local_iqtable).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
register(ejabberd_local,
Pid = proc_lib:spawn_link(ejabberd_local, init, [])),
{ok, Pid}.
init() ->
lists:foreach(
fun(Host) ->
ejabberd_router:register_route(Host, {apply, ?MODULE, route}),
ejabberd_hooks:add(local_send_to_resource_hook, Host,
?MODULE, bounce_resource_packet, 100)
end, ?MYHOSTS),
catch ets:new(?IQTABLE, [named_table, public]),
loop().
loop() ->
receive
{route, From, To, Packet} ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
loop();
{register_iq_handler, Host, XMLNS, Module, Function} ->
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}),
catch mod_disco:register_feature(Host, XMLNS),
loop();
{register_iq_handler, Host, XMLNS, Module, Function, Opts} ->
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function, Opts}),
catch mod_disco:register_feature(Host, XMLNS),
loop();
{unregister_iq_handler, Host, XMLNS} ->
case ets:lookup(?IQTABLE, {XMLNS, Host}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ ->
ok
end,
ets:delete(?IQTABLE, {XMLNS, Host}),
catch mod_disco:unregister_feature(Host, XMLNS),
loop();
refresh_iq_handlers ->
lists:foreach(
fun(T) ->
case T of
{{XMLNS, Host}, _Module, _Function, _Opts} ->
catch mod_disco:register_feature(Host, XMLNS);
{{XMLNS, Host}, _Module, _Function} ->
catch mod_disco:register_feature(Host, XMLNS);
_ ->
ok
end
end, ets:tab2list(?IQTABLE)),
loop();
_ ->
loop()
end.
do_route(From, To, Packet) ->
?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
[From, To, Packet, 8]),
if
To#jid.luser /= "" ->
ejabberd_sm:route(From, To, Packet);
To#jid.lresource == "" ->
{xmlelement, Name, Attrs, _Els} = Packet,
case Name of
"iq" ->
process_iq(From, To, Packet);
"message" ->
ok;
"presence" ->
ok;
_ ->
ok
end;
true ->
{xmlelement, Name, Attrs, _Els} = Packet,
case xml:get_attr_s("type", Attrs) of
"error" -> ok;
"result" -> ok;
_ ->
ejabberd_hooks:run(local_send_to_resource_hook,
To#jid.lserver,
[From, To, Packet])
end
end.
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
process_iq(From, To, Packet) ->
IQ = jlib:iq_query_info(Packet),
@@ -173,3 +100,146 @@ bounce_resource_packet(From, To, Packet) ->
Err = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND),
ejabberd_router:route(To, From, Err),
stop.
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
lists:foreach(
fun(Host) ->
ejabberd_router:register_route(Host, {apply, ?MODULE, route}),
ejabberd_hooks:add(local_send_to_resource_hook, Host,
?MODULE, bounce_resource_packet, 100)
end, ?MYHOSTS),
catch ets:new(?IQTABLE, [named_table, public]),
{ok, #state{}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}),
catch mod_disco:register_feature(Host, XMLNS),
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) ->
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function, Opts}),
catch mod_disco:register_feature(Host, XMLNS),
{noreply, State};
handle_info({unregister_iq_handler, Host, XMLNS}, State) ->
case ets:lookup(?IQTABLE, {XMLNS, Host}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ ->
ok
end,
ets:delete(?IQTABLE, {XMLNS, Host}),
catch mod_disco:unregister_feature(Host, XMLNS),
{noreply, State};
handle_info(refresh_iq_handlers, State) ->
lists:foreach(
fun(T) ->
case T of
{{XMLNS, Host}, _Module, _Function, _Opts} ->
catch mod_disco:register_feature(Host, XMLNS);
{{XMLNS, Host}, _Module, _Function} ->
catch mod_disco:register_feature(Host, XMLNS);
_ ->
ok
end
end, ets:tab2list(?IQTABLE)),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
do_route(From, To, Packet) ->
?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
[From, To, Packet, 8]),
if
To#jid.luser /= "" ->
ejabberd_sm:route(From, To, Packet);
To#jid.lresource == "" ->
{xmlelement, Name, _Attrs, _Els} = Packet,
case Name of
"iq" ->
process_iq(From, To, Packet);
"message" ->
ok;
"presence" ->
ok;
_ ->
ok
end;
true ->
{xmlelement, _Name, Attrs, _Els} = Packet,
case xml:get_attr_s("type", Attrs) of
"error" -> ok;
"result" -> ok;
_ ->
ejabberd_hooks:run(local_send_to_resource_hook,
To#jid.lserver,
[From, To, Packet])
end
end.
+26 -11
View File
@@ -29,7 +29,7 @@
%% Other
%%----------------------------------------------------------------------
init(File) ->
case file:open(File, [append]) of
case file:open(File, [append, raw]) of
{ok, Fd} ->
{ok, #state{fd = Fd, file = File}};
Error ->
@@ -66,7 +66,8 @@ handle_info({'EXIT', _Fd, _Reason}, _State) ->
remove_handler;
handle_info({emulator, _GL, reopen}, State) ->
file:close(State#state.fd),
case file:open(State#state.file, [append]) of
rotate_log(State#state.file),
case file:open(State#state.file, [append, raw]) of
{ok, Fd} ->
{ok, State#state{fd = Fd}};
Error ->
@@ -101,38 +102,38 @@ write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) ->
T = write_time(Time),
case catch io_lib:format(add_node(Format,Pid), Args) of
S when list(S) ->
io:format(Fd, T ++ S, []);
file:write(Fd, io_lib:format(T ++ S, []));
_ ->
F = add_node("ERROR: ~p - ~p~n", Pid),
io:format(Fd, T ++ F, [Format,Args])
file:write(Fd, io_lib:format(T ++ F, [Format,Args]))
end;
write_event(Fd, {Time, {emulator, _GL, Chars}}) ->
T = write_time(Time),
case catch io_lib:format(Chars, []) of
S when list(S) ->
io:format(Fd, T ++ S, []);
file:write(Fd, io_lib:format(T ++ S, []));
_ ->
io:format(Fd, T ++ "ERROR: ~p ~n", [Chars])
file:write(Fd, io_lib:format(T ++ "ERROR: ~p ~n", [Chars]))
end;
write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) ->
T = write_time(Time),
io:format(Fd, T ++ add_node("~p~n",Pid),[Info]);
file:write(Fd, io_lib:format(T ++ add_node("~p~n",Pid), [Info]));
write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) ->
T = write_time(Time),
S = format_report(Rep),
io:format(Fd, T ++ S ++ add_node("", Pid), []);
file:write(Fd, io_lib:format(T ++ S ++ add_node("", Pid), []));
write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) ->
T = write_time(Time, "INFO REPORT"),
S = format_report(Rep),
io:format(Fd, T ++ S ++ add_node("", Pid), []);
file:write(Fd, io_lib:format(T ++ S ++ add_node("", Pid), []));
write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
T = write_time(Time, "INFO REPORT"),
case catch io_lib:format(add_node(Format,Pid), Args) of
S when list(S) ->
io:format(Fd, T ++ S, []);
file:write(Fd, io_lib:format(T ++ S, []));
_ ->
F = add_node("ERROR: ~p - ~p~n", Pid),
io:format(Fd, T ++ F, [Format,Args])
file:write(Fd, io_lib:format(T ++ F, [Format,Args]))
end;
write_event(_, _) ->
ok.
@@ -189,3 +190,17 @@ write_time({{Y,Mo,D},{H,Mi,S}}, Type) ->
io_lib:format("~n=~s==== ~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w ===~n",
[Type, Y, Mo, D, H, Mi, S]).
%% Rename the log file if it the filename exists
%% This is needed in systems when the file must be closed before rotation (Windows).
%% On most Unix-like system, the file can be renamed from the command line and
%%the log can directly be reopened.
rotate_log(Filename) ->
case file:read_file_info(Filename) of
{ok, _FileInfo} ->
RotationName = filename:rootname(Filename),
file:rename(Filename, [RotationName, "-old.log"]),
ok;
{error, _Reason} ->
ok
end.
+240 -76
View File
@@ -10,21 +10,88 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-export([start/3,
receiver/4,
-behaviour(gen_server).
%% API
-export([start_link/5,
start/3,
start/4,
change_shaper/2,
reset_stream/1,
starttls/2]).
starttls/2,
compress/2,
become_controller/1,
close/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-record(state, {socket,
sock_mod,
shaper_state,
c2s_pid,
max_stanza_size,
xml_stream_state,
timeout}).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Socket, SockMod, Shaper, MaxStanzaSize, C2SPid) ->
gen_server:start_link(
?MODULE, [Socket, SockMod, Shaper, MaxStanzaSize, C2SPid], []).
%%--------------------------------------------------------------------
%% Function: start() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start(Socket, SockMod, Shaper) ->
proc_lib:spawn(?MODULE, receiver, [Socket, SockMod, Shaper, self()]).
start(Socket, SockMod, Shaper, infinity).
start(Socket, SockMod, Shaper, MaxStanzaSize) ->
{ok, Pid} = supervisor:start_child(
ejabberd_receiver_sup,
[Socket, SockMod, Shaper, MaxStanzaSize, self()]),
Pid.
receiver(Socket, SockMod, Shaper, C2SPid) ->
XMLStreamState = xml_stream:new(C2SPid),
change_shaper(Pid, Shaper) ->
gen_server:cast(Pid, {change_shaper, Shaper}).
reset_stream(Pid) ->
gen_server:call(Pid, reset_stream).
starttls(Pid, TLSSocket) ->
gen_server:call(Pid, {starttls, TLSSocket}).
compress(Pid, ZlibSocket) ->
gen_server:call(Pid, {compress, ZlibSocket}).
become_controller(Pid) ->
gen_server:call(Pid, become_controller).
close(Pid) ->
gen_server:cast(Pid, close).
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Socket, SockMod, Shaper, MaxStanzaSize, C2SPid]) ->
XMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
ShaperState = shaper:new(Shaper),
Timeout = case SockMod of
ssl ->
@@ -32,77 +99,174 @@ receiver(Socket, SockMod, Shaper, C2SPid) ->
_ ->
infinity
end,
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamState, Timeout).
{ok, #state{socket = Socket,
sock_mod = SockMod,
shaper_state = ShaperState,
c2s_pid = C2SPid,
max_stanza_size = MaxStanzaSize,
xml_stream_state = XMLStreamState,
timeout = Timeout}}.
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamState, Timeout) ->
Res = (catch SockMod:recv(Socket, 0, Timeout)),
receive
{starttls, TLSSocket} ->
xml_stream:close(XMLStreamState),
XMLStreamState1 = xml_stream:new(C2SPid),
TLSRes = case Res of
{ok, Data} ->
tls:recv_data(TLSSocket, Data);
_ ->
tls:recv_data(TLSSocket, "")
end,
receiver1(TLSSocket, tls,
ShaperState, C2SPid, XMLStreamState1, Timeout,
TLSRes);
{change_timeout, NewTimeout} -> % Dirty hack
receiver1(Socket, SockMod,
ShaperState, C2SPid, XMLStreamState, NewTimeout,
Res)
after 0 ->
receiver1(Socket, SockMod,
ShaperState, C2SPid, XMLStreamState, Timeout,
Res)
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({starttls, TLSSocket}, _From,
#state{xml_stream_state = XMLStreamState,
c2s_pid = C2SPid,
max_stanza_size = MaxStanzaSize} = State) ->
xml_stream:close(XMLStreamState),
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
NewState = State#state{socket = TLSSocket,
sock_mod = tls,
xml_stream_state = NewXMLStreamState},
case tls:recv_data(TLSSocket, "") of
{ok, TLSData} ->
{reply, ok, process_data(TLSData, NewState)};
{error, _Reason} ->
{stop, normal, ok, NewState}
end;
handle_call({compress, ZlibSocket}, _From,
#state{xml_stream_state = XMLStreamState,
c2s_pid = C2SPid,
max_stanza_size = MaxStanzaSize} = State) ->
xml_stream:close(XMLStreamState),
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
NewState = State#state{socket = ZlibSocket,
sock_mod = ejabberd_zlib,
xml_stream_state = NewXMLStreamState},
case ejabberd_zlib:recv_data(ZlibSocket, "") of
{ok, ZlibData} ->
{reply, ok, process_data(ZlibData, NewState)};
{error, _Reason} ->
{stop, normal, ok, NewState}
end;
handle_call(reset_stream, _From,
#state{xml_stream_state = XMLStreamState,
c2s_pid = C2SPid,
max_stanza_size = MaxStanzaSize} = State) ->
xml_stream:close(XMLStreamState),
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
Reply = ok,
{reply, Reply, State#state{xml_stream_state = NewXMLStreamState}};
handle_call(become_controller, _From, State) ->
activate_socket(State),
Reply = ok,
{reply, Reply, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({change_shaper, Shaper}, State) ->
NewShaperState = shaper:new(Shaper),
{noreply, State#state{shaper_state = NewShaperState}};
handle_cast(close, State) ->
{stop, normal, State};
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({Tag, _TCPSocket, Data},
#state{socket = Socket,
sock_mod = SockMod} = State)
when (Tag == tcp) or (Tag == ssl) ->
case SockMod of
tls ->
case tls:recv_data(Socket, Data) of
{ok, TLSData} ->
{noreply, process_data(TLSData, State)};
{error, _Reason} ->
{stop, normal, State}
end;
ejabberd_zlib ->
case ejabberd_zlib:recv_data(Socket, Data) of
{ok, ZlibData} ->
{noreply, process_data(ZlibData, State)};
{error, _Reason} ->
{stop, normal, State}
end;
_ ->
{noreply, process_data(Data, State)}
end;
handle_info({Tag, _TCPSocket}, State)
when (Tag == tcp_closed) or (Tag == ssl_closed) ->
{stop, normal, State};
handle_info({Tag, _TCPSocket, Reason}, State)
when (Tag == tcp_error) or (Tag == ssl_error) ->
case Reason of
timeout ->
{noreply, State};
_ ->
{stop, normal, State}
end;
handle_info({timeout, _Ref, activate}, State) ->
activate_socket(State),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, #state{xml_stream_state = XMLStreamState,
c2s_pid = C2SPid} = State) ->
xml_stream:close(XMLStreamState),
gen_fsm:send_event(C2SPid, closed),
catch (State#state.sock_mod):close(State#state.socket),
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
activate_socket(#state{socket = Socket,
sock_mod = SockMod}) ->
case SockMod of
gen_tcp ->
inet:setopts(Socket, [{active, once}]);
_ ->
SockMod:setopts(Socket, [{active, once}])
end.
receiver1(Socket, SockMod, ShaperState, C2SPid, XMLStreamState, Timeout, Res) ->
case Res of
{ok, Text} ->
ShaperSt1 = receive
{change_shaper, Shaper} ->
shaper:new(Shaper)
after 0 ->
ShaperState
end,
NewShaperState = shaper:update(ShaperSt1, size(Text)),
XMLStreamState1 = receive
reset_stream ->
xml_stream:close(XMLStreamState),
xml_stream:new(C2SPid)
after 0 ->
XMLStreamState
end,
XMLStreamState2 = xml_stream:parse(XMLStreamState1, Text),
receiver(Socket, SockMod, NewShaperState, C2SPid, XMLStreamState2,
Timeout);
{error, timeout} ->
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamState,
Timeout);
{error, Reason} ->
xml_stream:close(XMLStreamState),
gen_fsm:send_event(C2SPid, closed),
ok;
{'EXIT', Reason} ->
?ERROR_MSG("(~w) abnormal ~w:recv termination:~n\t~p~n",
[Socket, SockMod, Reason]),
xml_stream:close(XMLStreamState),
gen_fsm:send_event(C2SPid, closed),
ok
end.
change_shaper(Pid, Shaper) ->
Pid ! {change_shaper, Shaper}.
reset_stream(Pid) ->
Pid ! reset_stream.
starttls(Pid, TLSSocket) ->
Pid ! {starttls, TLSSocket}.
process_data(Data,
#state{xml_stream_state = XMLStreamState,
shaper_state = ShaperState} = State) ->
?DEBUG("Received XML on stream = ~p", [binary_to_list(Data)]),
XMLStreamState1 = xml_stream:parse(XMLStreamState, Data),
{NewShaperState, Pause} = shaper:update(ShaperState, size(Data)),
if
Pause > 0 ->
erlang:start_timer(Pause, self(), activate);
true ->
activate_socket(State)
end,
State#state{xml_stream_state = XMLStreamState1,
shaper_state = NewShaperState}.
+168 -100
View File
@@ -10,6 +10,9 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
%% API
-export([route/3,
register_route/1,
register_route/2,
@@ -20,114 +23,28 @@
dirty_get_all_domains/0
]).
-export([start_link/0, init/0]).
-export([start_link/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(route, {domain, pid, local_hint}).
-record(state, {}).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
update_tables(),
mnesia:create_table(route,
[{ram_copies, [node()]},
{type, bag},
{attributes,
record_info(fields, route)}]),
mnesia:add_table_copy(route, node(), ram_copies),
Pid = proc_lib:spawn_link(ejabberd_router, init, []),
register(ejabberd_router, Pid),
{ok, Pid}.
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init() ->
mnesia:subscribe({table, route, simple}),
lists:foreach(
fun(Pid) ->
erlang:monitor(process, Pid)
end,
mnesia:dirty_select(route, [{{route, '_', '$1', '_'}, [], ['$1']}])),
loop().
loop() ->
receive
{route, From, To, Packet} ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
loop();
{mnesia_table_event, {write, #route{pid = Pid}, _ActivityId}} ->
erlang:monitor(process, Pid),
loop();
{'DOWN', _Ref, _Type, Pid, _Info} ->
F = fun() ->
Es = mnesia:select(
route,
[{#route{pid = Pid, _ = '_'},
[],
['$_']}]),
lists:foreach(fun(E) ->
mnesia:delete_object(E)
end, Es)
end,
mnesia:transaction(F),
loop();
_ ->
loop()
end.
do_route(OrigFrom, OrigTo, OrigPacket) ->
?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n",
[OrigFrom, OrigTo, OrigPacket]),
LOrigDstDomain = OrigTo#jid.lserver,
case ejabberd_hooks:run_fold(filter_packet, LOrigDstDomain,
{OrigFrom, OrigTo, OrigPacket}, []) of
{From, To, Packet} ->
LDstDomain = To#jid.lserver,
case mnesia:dirty_read(route, LDstDomain) of
[] ->
ejabberd_s2s:route(From, To, Packet);
[R] ->
Pid = R#route.pid,
if
node(Pid) == node() ->
case R#route.local_hint of
{apply, Module, Function} ->
Module:Function(From, To, Packet);
_ ->
Pid ! {route, From, To, Packet}
end;
true ->
Pid ! {route, From, To, Packet}
end;
Rs ->
case [R || R <- Rs, node(R#route.pid) == node()] of
[] ->
R = lists:nth(erlang:phash(now(), length(Rs)), Rs),
Pid = R#route.pid,
Pid ! {route, From, To, Packet};
LRs ->
LRs,
R = lists:nth(erlang:phash(now(), length(LRs)), LRs),
Pid = R#route.pid,
case R#route.local_hint of
{apply, Module, Function} ->
Module:Function(From, To, Packet);
_ ->
Pid ! {route, From, To, Packet}
end
end
end;
drop ->
ok
end.
%route(From, To, Packet) ->
% ejabberd_router ! {route, From, To, Packet}.
route(From, To, Packet) ->
case catch do_route(From, To, Packet) of
@@ -196,6 +113,157 @@ dirty_get_all_domains() ->
lists:usort(mnesia:dirty_all_keys(route)).
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
update_tables(),
mnesia:create_table(route,
[{ram_copies, [node()]},
{type, bag},
{attributes,
record_info(fields, route)}]),
mnesia:add_table_copy(route, node(), ram_copies),
mnesia:subscribe({table, route, simple}),
lists:foreach(
fun(Pid) ->
erlang:monitor(process, Pid)
end,
mnesia:dirty_select(route, [{{route, '_', '$1', '_'}, [], ['$1']}])),
{ok, #state{}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
{noreply, State};
handle_info({mnesia_table_event, {write, #route{pid = Pid}, _ActivityId}},
State) ->
erlang:monitor(process, Pid),
{noreply, State};
handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) ->
F = fun() ->
Es = mnesia:select(
route,
[{#route{pid = Pid, _ = '_'},
[],
['$_']}]),
lists:foreach(fun(E) ->
mnesia:delete_object(E)
end, Es)
end,
mnesia:transaction(F),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
do_route(OrigFrom, OrigTo, OrigPacket) ->
?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n",
[OrigFrom, OrigTo, OrigPacket]),
LOrigDstDomain = OrigTo#jid.lserver,
case ejabberd_hooks:run_fold(filter_packet, LOrigDstDomain,
{OrigFrom, OrigTo, OrigPacket}, []) of
{From, To, Packet} ->
LDstDomain = To#jid.lserver,
case mnesia:dirty_read(route, LDstDomain) of
[] ->
ejabberd_s2s:route(From, To, Packet);
[R] ->
Pid = R#route.pid,
if
node(Pid) == node() ->
case R#route.local_hint of
{apply, Module, Function} ->
Module:Function(From, To, Packet);
_ ->
Pid ! {route, From, To, Packet}
end;
true ->
Pid ! {route, From, To, Packet}
end;
Rs ->
case [R || R <- Rs, node(R#route.pid) == node()] of
[] ->
R = lists:nth(erlang:phash(now(), length(Rs)), Rs),
Pid = R#route.pid,
Pid ! {route, From, To, Packet};
LRs ->
LRs,
R = lists:nth(erlang:phash(now(), length(LRs)), LRs),
Pid = R#route.pid,
case R#route.local_hint of
{apply, Module, Function} ->
Module:Function(From, To, Packet);
_ ->
Pid ! {route, From, To, Packet}
end
end
end;
drop ->
ok
end.
update_tables() ->
case catch mnesia:table_info(route, attributes) of
+112 -55
View File
@@ -10,7 +10,10 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-export([start_link/0, init/0,
-behaviour(gen_server).
%% API
-export([start_link/0,
route/3,
have_connection/1,
get_key/1,
@@ -18,43 +21,25 @@
remove_connection/1,
dirty_get_connections/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(s2s, {fromto, pid, key}).
-record(state, {}).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
Pid = proc_lib:spawn_link(ejabberd_s2s, init, []),
register(ejabberd_s2s, Pid),
{ok, Pid}.
init() ->
update_tables(),
mnesia:create_table(s2s, [{ram_copies, [node()]},
{attributes, record_info(fields, s2s)}]),
mnesia:add_table_copy(s2s, node(), ram_copies),
mnesia:subscribe(system),
loop().
loop() ->
receive
{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~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
loop();
_ ->
loop()
end.
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
route(From, To, Packet) ->
case catch do_route(From, To, Packet) of
@@ -65,28 +50,12 @@ route(From, To, Packet) ->
ok
end.
remove_connection(FromTo) ->
F = fun() ->
mnesia:delete({s2s, FromTo})
end,
mnesia:transaction(F).
clean_table_from_bad_node(Node) ->
F = fun() ->
Es = mnesia:select(
s2s,
[{#s2s{pid = '$1', _ = '_'},
[{'==', {node, '$1'}, Node}],
['$_']}]),
lists:foreach(fun(E) ->
mnesia:delete_object(E)
end, Es)
end,
mnesia:transaction(F).
have_connection(FromTo) ->
case catch mnesia:dirty_read(s2s, FromTo) of
[_] ->
@@ -123,9 +92,103 @@ try_register(FromTo) ->
false
end.
dirty_get_connections() ->
mnesia:dirty_all_keys(s2s).
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
update_tables(),
mnesia:create_table(s2s, [{ram_copies, [node()]},
{attributes, record_info(fields, s2s)}]),
mnesia:add_table_copy(s2s, node(), ram_copies),
mnesia:subscribe(system),
{ok, #state{}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
clean_table_from_bad_node(Node),
{noreply, State};
handle_info({route, From, To, Packet}, State) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
clean_table_from_bad_node(Node) ->
F = fun() ->
Es = mnesia:select(
s2s,
[{#s2s{pid = '$1', _ = '_'},
[{'==', {node, '$1'}, Node}],
['$_']}]),
lists:foreach(fun(E) ->
mnesia:delete_object(E)
end, Es)
end,
mnesia:transaction(F).
do_route(From, To, Packet) ->
?DEBUG("s2s manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
@@ -174,15 +237,9 @@ find_connection(From, To) ->
{atomic, El#s2s.pid}
end.
send_element(Pid, El) ->
Pid ! {send_element, El}.
dirty_get_connections() ->
mnesia:dirty_all_keys(s2s).
update_tables() ->
case catch mnesia:table_info(s2s, attributes) of
[fromto, node, key] ->
+29 -10
View File
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_s2s_in.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose :
%%% Purpose : Serve incoming s2s connection
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
@@ -14,7 +14,9 @@
%% External exports
-export([start/2,
start_link/2,match_domain/2]).
start_link/2,
become_controller/1,
match_domain/2]).
%% gen_fsm callbacks
-export([init/1,
@@ -29,9 +31,8 @@
-include("ejabberd.hrl").
-include("jlib.hrl").
%-include_lib("ssl/pkix/SSL-PKIX.hrl").
-include_lib("ssl/pkix/PKIX1Explicit88.hrl").
-include_lib("ssl/pkix/PKIX1Implicit88.hrl").
-include_lib("ssl/include/PKIX1Explicit88.hrl").
-include_lib("ssl/include/PKIX1Implicit88.hrl").
-include("XmppAddr.hrl").
-define(DICT, dict).
@@ -87,6 +88,9 @@ start(SockData, Opts) ->
start_link(SockData, Opts) ->
gen_fsm:start_link(ejabberd_s2s_in, [SockData, Opts], ?FSMOPTS).
become_controller(Pid) ->
gen_fsm:send_all_state_event(Pid, become_controller).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
@@ -100,11 +104,17 @@ start_link(SockData, Opts) ->
%%----------------------------------------------------------------------
init([{SockMod, Socket}, Opts]) ->
?INFO_MSG("started: ~p", [{SockMod, Socket}]),
ReceiverPid = ejabberd_receiver:start(Socket, SockMod, none),
Shaper = case lists:keysearch(shaper, 1, Opts) of
{value, {_, S}} -> S;
_ -> none
end,
MaxStanzaSize =
case lists:keysearch(max_stanza_size, 1, Opts) of
{value, {_, Size}} -> Size;
_ -> infinity
end,
ReceiverPid = ejabberd_receiver:start(
Socket, SockMod, none, MaxStanzaSize),
StartTLS = case ejabberd_config:get_local_option(s2s_use_starttls) of
undefined ->
false;
@@ -342,7 +352,8 @@ stream_established({xmlstreamelement, El}, StateData) ->
[]}),
{next_state, stream_established, StateData#state{timer = Timer}};
_ ->
{xmlelement, Name, Attrs, _Els} = El,
NewEl = jlib:remove_attr("xmlns", El),
{xmlelement, Name, Attrs, _Els} = NewEl,
From_s = xml:get_attr_s("from", Attrs),
From = jlib:string_to_jid(From_s),
To_s = xml:get_attr_s("to", Attrs),
@@ -362,7 +373,8 @@ stream_established({xmlstreamelement, El}, StateData) ->
if ((Name == "iq") or
(Name == "message") or
(Name == "presence")) ->
ejabberd_router:route(From, To, El);
ejabberd_router:route(
From, To, NewEl);
true ->
error
end;
@@ -376,7 +388,8 @@ stream_established({xmlstreamelement, El}, StateData) ->
if ((Name == "iq") or
(Name == "message") or
(Name == "presence")) ->
ejabberd_router:route(From, To, El);
ejabberd_router:route(
From, To, NewEl);
true ->
error
end;
@@ -455,6 +468,12 @@ stream_established(closed, StateData) ->
%% {next_state, NextStateName, NextStateData, Timeout} |
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
handle_event(become_controller, StateName, StateData) ->
ok = (StateData#state.sockmod):controlling_process(
StateData#state.socket,
StateData#state.receiver),
ejabberd_receiver:become_controller(StateData#state.receiver),
{next_state, StateName, StateData};
handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
@@ -499,7 +518,7 @@ handle_info(_, StateName, StateData) ->
%%----------------------------------------------------------------------
terminate(Reason, _StateName, StateData) ->
?INFO_MSG("terminated: ~p", [Reason]),
(StateData#state.sockmod):close(StateData#state.socket),
ejabberd_receiver:close(StateData#state.receiver),
ok.
%%%----------------------------------------------------------------------
+108 -49
View File
@@ -25,12 +25,14 @@
wait_for_features/2,
wait_for_auth_result/2,
wait_for_starttls_proceed/2,
reopen_socket/2,
stream_established/2,
handle_event/3,
handle_sync_event/4,
handle_info/3,
terminate/3,
code_change/4]).
code_change/4,
test_get_addr_port/1]).
-include("ejabberd.hrl").
-include("jlib.hrl").
@@ -140,7 +142,44 @@ init([From, Server, Type]) ->
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
open_socket(init, StateData) ->
{Addr, Port} = get_addr_port(StateData#state.server),
AddrList = get_addr_port(StateData#state.server),
case lists:foldl(fun({Addr, Port}, Acc) ->
case Acc of
{ok, Socket} ->
{ok, Socket};
_ ->
open_socket1(Addr, Port)
end
end, {error, badarg}, AddrList) of
{ok, Socket} ->
ReceiverPid = ejabberd_receiver:start(Socket, gen_tcp, none),
ok = gen_tcp:controlling_process(Socket, ReceiverPid),
ejabberd_receiver:become_controller(ReceiverPid),
Version = if
StateData#state.use_v10 ->
" version='1.0'";
true ->
""
end,
NewStateData = StateData#state{socket = Socket,
sockmod = gen_tcp,
tls_enabled = false,
receiver = ReceiverPid,
streamid = new_id()},
send_text(NewStateData, io_lib:format(?STREAM_HEADER,
[StateData#state.server,
Version])),
{next_state, wait_for_stream, NewStateData};
{error, _Reason} ->
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
bounce_messages(Error),
{stop, normal, StateData}
end;
open_socket(_, StateData) ->
{next_state, open_socket, StateData}.
%%----------------------------------------------------------------------
open_socket1(Addr, Port) ->
Res = case idna:domain_utf8_to_ascii(Addr) of
false -> {error, badarg};
ASCIIAddr ->
@@ -163,35 +202,16 @@ open_socket(init, StateData) ->
end,
case Res of
{ok, Socket} ->
ReceiverPid = ejabberd_receiver:start(Socket, gen_tcp, none),
Version = if
StateData#state.use_v10 ->
" version='1.0'";
true ->
""
end,
NewStateData = StateData#state{socket = Socket,
sockmod = gen_tcp,
tls_enabled = false,
receiver = ReceiverPid,
streamid = new_id()},
send_text(NewStateData, io_lib:format(?STREAM_HEADER,
[StateData#state.server,
Version])),
{next_state, wait_for_stream, NewStateData};
{ok, Socket};
{error, Reason} ->
?DEBUG("s2s_out: inet6 connect return ~p~n", [Reason]),
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
bounce_messages(Error),
{stop, normal, StateData};
{error, Reason};
{'EXIT', Reason} ->
?DEBUG("s2s_out: inet6 connect crashed ~p~n", [Reason]),
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
bounce_messages(Error),
{stop, normal, StateData}
end;
open_socket(_, StateData) ->
{next_state, open_socket, StateData}.
{error, Reason}
end.
%%----------------------------------------------------------------------
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
@@ -207,10 +227,9 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
StateData#state.server}]),
% TODO: clear message queue
(StateData#state.sockmod):close(StateData#state.socket),
gen_fsm:send_event(self(), init),
{next_state, open_socket, StateData#state{socket = undefined,
use_v10 = false}};
ejabberd_receiver:close(StateData#state.receiver),
{next_state, reopen_socket, StateData#state{socket = undefined,
use_v10 = false}};
_ ->
send_text(StateData, ?INVALID_NAMESPACE_ERR),
{stop, normal, StateData}
@@ -342,7 +361,6 @@ wait_for_features({xmlstreamelement, El}, StateData) ->
StateData#state{try_auth = false}};
StartTLS and StateData#state.tls and
(not StateData#state.tls_enabled) ->
StateData#state.receiver ! {change_timeout, 100},
send_element(StateData,
{xmlelement, "starttls",
[{"xmlns", ?NS_TLS}], []}),
@@ -350,9 +368,8 @@ wait_for_features({xmlstreamelement, El}, StateData) ->
StartTLSRequired and (not StateData#state.tls) ->
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
StateData#state.server}]),
(StateData#state.sockmod):close(StateData#state.socket),
gen_fsm:send_event(self(), init),
{next_state, open_socket,
ejabberd_receiver:close(StateData#state.receiver),
{next_state, reopen_socket,
StateData#state{socket = undefined,
use_v10 = false}};
true ->
@@ -408,9 +425,8 @@ wait_for_auth_result({xmlstreamelement, El}, StateData) ->
?NS_SASL ->
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
StateData#state.server}]),
(StateData#state.sockmod):close(StateData#state.socket),
gen_fsm:send_event(self(), init),
{next_state, open_socket,
ejabberd_receiver:close(StateData#state.receiver),
{next_state, reopen_socket,
StateData#state{socket = undefined}};
_ ->
send_text(StateData,
@@ -462,7 +478,6 @@ wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
ejabberd_receiver:starttls(
StateData#state.receiver, TLSSocket),
StateData#state.receiver ! {change_timeout, infinity},
NewStateData = StateData#state{sockmod = tls,
socket = TLSSocket,
streamid = new_id(),
@@ -498,6 +513,19 @@ wait_for_starttls_proceed(closed, StateData) ->
{stop, normal, StateData}.
reopen_socket({xmlstreamelement, El}, StateData) ->
{next_state, reopen_socket, StateData};
reopen_socket({xmlstreamend, Name}, StateData) ->
{next_state, reopen_socket, StateData};
reopen_socket({xmlstreamerror, _}, StateData) ->
{next_state, reopen_socket, StateData};
reopen_socket(timeout, StateData) ->
{stop, normal, StateData};
reopen_socket(closed, StateData) ->
gen_fsm:send_event(self(), init),
{next_state, open_socket, StateData}.
stream_established({xmlstreamelement, El}, StateData) ->
?INFO_MSG("stream established", []),
case is_verify_res(El) of
@@ -630,8 +658,7 @@ handle_info(_, StateName, StateData) ->
%%----------------------------------------------------------------------
terminate(Reason, StateName, StateData) ->
?INFO_MSG("terminated: ~p", [Reason]),
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
bounce_queue(StateData#state.queue, Error),
bounce_queue(StateData#state.queue, ?ERR_REMOTE_SERVER_NOT_FOUND),
case StateData#state.new of
false ->
ok;
@@ -642,8 +669,8 @@ terminate(Reason, StateName, StateData) ->
case StateData#state.socket of
undefined ->
ok;
Socket ->
(StateData#state.sockmod):close(Socket)
_Socket ->
ejabberd_receiver:close(StateData#state.receiver)
end,
ok.
@@ -779,18 +806,50 @@ get_addr_port(Server) ->
case Res of
{error, Reason} ->
?DEBUG("srv lookup of '~s' failed: ~p~n", [Server, Reason]),
{Server, ejabberd_config:get_local_option(outgoing_s2s_port)};
[{Server, ejabberd_config:get_local_option(outgoing_s2s_port)}];
{ok, HEnt} ->
?DEBUG("srv lookup of '~s': ~p~n",
[Server, HEnt#hostent.h_addr_list]),
case HEnt#hostent.h_addr_list of
[] ->
{Server,
ejabberd_config:get_local_option(outgoing_s2s_port)};
[{_, _, Port, Host} | _] ->
{Host, Port}
[{Server,
ejabberd_config:get_local_option(outgoing_s2s_port)}];
AddrList ->
% Probabilities are not exactly proportional to weights
% for simplicity (higher weigths are overvalued)
{A1, A2, A3} = now(),
random:seed(A1, A2, A3),
case (catch lists:map(
fun({Priority, Weight, Port, Host}) ->
N = case Weight of
0 -> 0;
_ -> (Weight + 1) * random:uniform()
end,
{Priority * 65536 - N, Host, Port}
end, AddrList)) of
{'EXIT', _Reasn} ->
[{Server,
ejabberd_config:get_local_option(outgoing_s2s_port)}];
SortedList ->
List = lists:map(
fun({_, Host, Port}) ->
{Host, Port}
end, lists:keysort(1, SortedList)),
?DEBUG("srv lookup of '~s': ~p~n", [Server, List]),
List
end
end
end.
test_get_addr_port(Server) ->
lists:foldl(
fun(_, Acc) ->
[HostPort | _] = get_addr_port(Server),
case lists:keysearch(HostPort, 1, Acc) of
false ->
[{HostPort, 1} | Acc];
{value, {_, Num}} ->
lists:keyreplace(HostPort, 1, Acc, {HostPort, Num + 1})
end
end, [], lists:seq(1, 100000)).
+22 -23
View File
@@ -13,7 +13,11 @@
-behaviour(gen_fsm).
%% External exports
-export([start/2, start_link/2, receiver/3, send_text/2, send_element/2]).
-export([start/2,
start_link/2,
send_text/2,
send_element/2,
become_controller/1]).
%% gen_fsm callbacks
-export([init/1,
@@ -75,6 +79,9 @@ start(SockData, Opts) ->
start_link(SockData, Opts) ->
gen_fsm:start_link(ejabberd_service, [SockData, Opts], ?FSMOPTS).
become_controller(Pid) ->
gen_fsm:send_all_state_event(Pid, become_controller).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
@@ -116,7 +123,7 @@ init([{SockMod, Socket}, Opts]) ->
false
end
end,
ReceiverPid = spawn(?MODULE, receiver, [Socket, SockMod, self()]),
ReceiverPid = ejabberd_receiver:start(Socket, SockMod, none),
{ok, wait_for_stream, #state{socket = Socket,
receiver = ReceiverPid,
streamid = new_id(),
@@ -171,11 +178,11 @@ wait_for_handshake({xmlstreamelement, El}, StateData) ->
end, StateData#state.hosts),
{next_state, stream_established, StateData};
_ ->
send_text(StateData, ?INVALID_HEADER_ERR),
send_text(StateData, ?INVALID_HANDSHAKE_ERR),
{stop, normal, StateData}
end;
_ ->
{next_state, wait_for_key, StateData}
{next_state, wait_for_handshake, StateData}
end;
wait_for_handshake({xmlstreamend, _Name}, StateData) ->
@@ -190,7 +197,8 @@ wait_for_handshake(closed, StateData) ->
stream_established({xmlstreamelement, El}, StateData) ->
{xmlelement, Name, Attrs, _Els} = El,
NewEl = jlib:remove_attr("xmlns", El),
{xmlelement, Name, Attrs, _Els} = NewEl,
From = xml:get_attr_s("from", Attrs),
FromJID1 = jlib:string_to_jid(From),
FromJID = case FromJID1 of
@@ -210,9 +218,9 @@ stream_established({xmlstreamelement, El}, StateData) ->
(Name == "message") or
(Name == "presence")) and
(ToJID /= error) and (FromJID /= error) ->
ejabberd_router:route(FromJID, ToJID, El);
ejabberd_router:route(FromJID, ToJID, NewEl);
true ->
Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST),
Err = jlib:make_error_reply(NewEl, ?ERR_BAD_REQUEST),
send_element(StateData, Err),
error
end,
@@ -251,6 +259,12 @@ stream_established(closed, StateData) ->
%% {next_state, NextStateName, NextStateData, Timeout} |
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
handle_event(become_controller, StateName, StateData) ->
ok = (StateData#state.sockmod):controlling_process(
StateData#state.socket,
StateData#state.receiver),
ejabberd_receiver:become_controller(StateData#state.receiver),
{next_state, StateName, StateData};
handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
@@ -314,28 +328,13 @@ terminate(Reason, StateName, StateData) ->
_ ->
ok
end,
(StateData#state.sockmod):close(StateData#state.socket),
ejabberd_receiver:close(StateData#state.receiver),
ok.
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
receiver(Socket, SockMod, C2SPid) ->
XMLStreamPid = xml_stream:start(C2SPid),
receiver(Socket, SockMod, C2SPid, XMLStreamPid).
receiver(Socket, SockMod, C2SPid, XMLStreamPid) ->
case SockMod:recv(Socket, 0) of
{ok, Text} ->
xml_stream:send_text(XMLStreamPid, Text),
receiver(Socket, SockMod, C2SPid, XMLStreamPid);
{error, _Reason} ->
exit(XMLStreamPid, closed),
gen_fsm:send_event(C2SPid, closed),
ok
end.
send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket,Text).
+319 -193
View File
@@ -10,87 +10,47 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-export([start_link/0, init/0,
-behaviour(gen_server).
%% API
-export([start_link/0,
route/3,
open_session/3, close_session/3,
open_session/4, close_session/4,
bounce_offline_message/3,
disconnect_removed_user/2,
get_user_resources/2,
set_presence/4,
unset_presence/4,
set_presence/5,
unset_presence/5,
close_session_unset_presence/5,
dirty_get_sessions_list/0,
dirty_get_my_sessions_list/0,
get_vh_session_list/1,
register_iq_handler/4,
register_iq_handler/5,
unregister_iq_handler/2
unregister_iq_handler/2,
ctl_process/2
]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("ejabberd_ctl.hrl").
-record(session, {usr, us, pid}).
-record(presence, {usr, us, priority}).
-record(session, {sid, usr, us, priority}).
-record(state, {}).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
Pid = proc_lib:spawn_link(ejabberd_sm, init, []),
register(ejabberd_sm, Pid),
{ok, Pid}.
init() ->
update_tables(),
mnesia:create_table(session, [{ram_copies, [node()]},
{attributes, record_info(fields, session)}]),
mnesia:add_table_index(session, us),
mnesia:add_table_copy(session, node(), ram_copies),
mnesia:create_table(presence,
[{ram_copies, [node()]},
{attributes, record_info(fields, presence)}]),
mnesia:add_table_index(presence, us),
mnesia:subscribe(system),
ets:new(sm_iqtable, [named_table]),
lists:foreach(
fun(Host) ->
ejabberd_hooks:add(offline_message_hook, Host,
ejabberd_sm, bounce_offline_message, 100),
ejabberd_hooks:add(remove_user, Host,
ejabberd_sm, disconnect_removed_user, 100)
end, ?MYHOSTS),
loop().
loop() ->
receive
{route, From, To, Packet} ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
loop();
{mnesia_system_event, {mnesia_down, Node}} ->
clean_table_from_bad_node(Node),
loop();
{register_iq_handler, Host, XMLNS, Module, Function} ->
ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function}),
loop();
{register_iq_handler, Host, XMLNS, Module, Function, Opts} ->
ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function, Opts}),
loop();
{unregister_iq_handler, Host, XMLNS} ->
case ets:lookup(sm_iqtable, {XMLNS, Host}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ ->
ok
end,
ets:delete(sm_iqtable, {XMLNS, Host}),
loop();
_ ->
loop()
end.
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
route(From, To, Packet) ->
case catch do_route(From, To, Packet) of
@@ -101,59 +61,245 @@ route(From, To, Packet) ->
ok
end.
open_session(User, Server, Resource) ->
register_connection(User, Server, Resource, self()).
open_session(SID, User, Server, Resource) ->
set_session(SID, User, Server, Resource, undefined),
JID = jlib:make_jid(User, Server, Resource),
ejabberd_hooks:run(sm_register_connection_hook, JID#jid.lserver,
[SID, JID]).
close_session(User, Server, Resource) ->
remove_connection(User, Server, Resource).
close_session(SID, User, Server, Resource) ->
F = fun() ->
mnesia:delete({session, SID})
end,
mnesia:sync_dirty(F),
JID = jlib:make_jid(User, Server, Resource),
ejabberd_hooks:run(sm_remove_connection_hook, JID#jid.lserver,
[SID, JID]).
bounce_offline_message(From, To, Packet) ->
Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
ejabberd_router:route(To, From, Err),
stop.
disconnect_removed_user(User, Server) ->
ejabberd_sm:route(jlib:make_jid("", "", ""),
jlib:make_jid(User, Server, ""),
{xmlelement, "broadcast", [],
[{exit, "User removed"}]}).
get_user_resources(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_index_read(session, US, #session.us) of
{'EXIT', _Reason} ->
[];
Ss ->
[element(3, S#session.usr) || S <- clean_session_list(Ss)]
end.
set_presence(SID, User, Server, Resource, Priority) ->
set_session(SID, User, Server, Resource, Priority).
unset_presence(SID, User, Server, Resource, Status) ->
set_session(SID, User, Server, Resource, undefined),
ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
[User, Server, Resource, Status]).
close_session_unset_presence(SID, User, Server, Resource, Status) ->
close_session(SID, User, Server, Resource),
ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
[User, Server, Resource, Status]).
register_connection(User, Server, Resource, Pid) ->
dirty_get_sessions_list() ->
mnesia:dirty_select(
session,
[{#session{usr = '$1', _ = '_'},
[],
['$1']}]).
dirty_get_my_sessions_list() ->
mnesia:dirty_select(
session,
[{#session{sid = {'_', '$1'}, _ = '_'},
[{'==', {node, '$1'}, node()}],
['$_']}]).
get_vh_session_list(Server) ->
LServer = jlib:nameprep(Server),
mnesia:dirty_select(
session,
[{#session{usr = '$1', _ = '_'},
[{'==', {element, 2, '$1'}, LServer}],
['$1']}]).
register_iq_handler(Host, XMLNS, Module, Fun) ->
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun}.
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}.
unregister_iq_handler(Host, XMLNS) ->
ejabberd_sm ! {unregister_iq_handler, Host, XMLNS}.
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
update_tables(),
mnesia:create_table(session,
[{ram_copies, [node()]},
{attributes, record_info(fields, session)}]),
mnesia:add_table_index(session, usr),
mnesia:add_table_index(session, us),
mnesia:add_table_copy(session, node(), ram_copies),
mnesia:subscribe(system),
ets:new(sm_iqtable, [named_table]),
lists:foreach(
fun(Host) ->
ejabberd_hooks:add(offline_message_hook, Host,
ejabberd_sm, bounce_offline_message, 100),
ejabberd_hooks:add(remove_user, Host,
ejabberd_sm, disconnect_removed_user, 100)
end, ?MYHOSTS),
ejabberd_ctl:register_commands(
[{"connected-users", "list all established sessions"},
{"connected-users-number", "print a number of established sessions"},
{"user-resources user server", "print user's connected resources"}],
?MODULE, ctl_process),
{ok, #state{}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
{noreply, State};
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
clean_table_from_bad_node(Node),
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function}),
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) ->
ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function, Opts}),
{noreply, State};
handle_info({unregister_iq_handler, Host, XMLNS}, State) ->
case ets:lookup(sm_iqtable, {XMLNS, Host}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ ->
ok
end,
ets:delete(sm_iqtable, {XMLNS, Host}),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
set_session(SID, User, Server, Resource, Priority) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LResource = jlib:resourceprep(Resource),
US = {LUser, LServer},
USR = {LUser, LServer, LResource},
F = fun() ->
Ss = mnesia:wread({session, USR}),
mnesia:write(#session{usr = USR, us = US, pid = Pid}),
Ss
end,
case mnesia:transaction(F) of
{atomic, Ss} ->
mnesia:write(#session{sid = SID,
usr = USR,
us = US,
priority = Priority})
end,
mnesia:sync_dirty(F),
SIDs = mnesia:dirty_select(
session,
[{#session{sid = '$1', usr = USR, _ = '_'}, [], ['$1']}]),
if
SIDs == [] ->
ok;
true ->
MaxSID = lists:max(SIDs),
lists:foreach(
fun(R) ->
R#session.pid ! replaced
end, Ss);
_ ->
false
fun({_, Pid} = S) when S /= MaxSID ->
Pid ! replaced;
(_) ->
ok
end, SIDs)
end.
remove_connection(User, Server, Resource) ->
LUser = jlib:nodeprep(User),
LResource = jlib:resourceprep(Resource),
LServer = jlib:nameprep(Server),
USR = {LUser, LServer, LResource},
F = fun() ->
mnesia:delete({session, USR})
end,
mnesia:transaction(F).
clean_table_from_bad_node(Node) ->
F = fun() ->
Es = mnesia:select(
session,
[{#session{pid = '$1', _ = '_'},
[{#session{sid = {'_', '$1'}, _ = '_'},
[{'==', {node, '$1'}, Node}],
['$_']}]),
lists:foreach(fun(E) ->
mnesia:delete_object(E),
mnesia:delete({presence, E#session.usr})
mnesia:delete({session, E#session.sid})
end, Es)
end,
mnesia:transaction(F).
mnesia:sync_dirty(F).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -257,7 +403,7 @@ do_route(From, To, Packet) ->
end;
_ ->
USR = {LUser, LServer, LResource},
case mnesia:dirty_read({session, USR}) of
case mnesia:dirty_index_read(session, USR, #session.usr) of
[] ->
case Name of
"message" ->
@@ -275,8 +421,9 @@ do_route(From, To, Packet) ->
_ ->
?DEBUG("packet droped~n", [])
end;
[Sess] ->
Pid = Sess#session.pid,
Ss ->
Session = lists:max(Ss),
Pid = element(2, Session#session.sid),
?DEBUG("sending to process ~p~n", [Pid]),
Pid ! {route, From, To, Packet}
end
@@ -285,19 +432,29 @@ do_route(From, To, Packet) ->
route_message(From, To, Packet) ->
LUser = To#jid.luser,
LServer = To#jid.lserver,
case catch lists:max(get_user_present_resources(LUser, LServer)) of
{Priority, R} when is_integer(Priority),
Priority >= 0 ->
LResource = jlib:resourceprep(R),
USR = {LUser, LServer, LResource},
case mnesia:dirty_read({session, USR}) of
[] ->
ok; % Race condition
[Sess] ->
Pid = Sess#session.pid,
?DEBUG("sending to process ~p~n", [Pid]),
Pid ! {route, From, To, Packet}
end;
PrioRes = get_user_present_resources(LUser, LServer),
case catch lists:max(PrioRes) of
{Priority, _R} when is_integer(Priority), Priority >= 0 ->
lists:foreach(
%% Route messages to all priority that equals the max, if
%% positive
fun({P, R}) when P == Priority ->
LResource = jlib:resourceprep(R),
USR = {LUser, LServer, LResource},
case mnesia:dirty_index_read(session, USR, #session.usr) of
[] ->
ok; % Race condition
Ss ->
Session = lists:max(Ss),
Pid = element(2, Session#session.sid),
?DEBUG("sending to process ~p~n", [Pid]),
Pid ! {route, From, To, Packet}
end;
%% Ignore other priority:
({_Prio, _Res}) ->
ok
end,
PrioRes);
_ ->
case xml:get_tag_attr_s("type", Packet) of
"error" ->
@@ -316,86 +473,43 @@ route_message(From, To, 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.
disconnect_removed_user(User, Server) ->
ejabberd_sm:route(jlib:make_jid("", "", ""),
jlib:make_jid(User, Server, ""),
{xmlelement, "broadcast", [],
[{exit, "User removed"}]}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clean_session_list(Ss) ->
clean_session_list(lists:keysort(#session.usr, Ss), []).
clean_session_list([], Res) ->
Res;
clean_session_list([S], Res) ->
[S | Res];
clean_session_list([S1, S2 | Rest], Res) ->
if
S1#session.usr == S2#session.usr ->
if
S1#session.sid > S2#session.sid ->
clean_session_list([S1 | Rest], Res);
true ->
clean_session_list([S2 | Rest], Res)
end;
true ->
clean_session_list([S2 | Rest], [S1 | Res])
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
get_user_resources(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
get_user_present_resources(LUser, LServer) ->
US = {LUser, LServer},
case catch mnesia:dirty_index_read(session, US, #session.us) of
{'EXIT', _Reason} ->
[];
Rs ->
lists:map(fun(R) ->
element(3, R#session.usr)
end, Rs)
Ss ->
[{S#session.priority, element(3, S#session.usr)} ||
S <- clean_session_list(Ss), is_integer(S#session.priority)]
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
set_presence(User, Server, Resource, Priority) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
USR = {User, Server, Resource},
US = {LUser, LServer},
F = fun() ->
mnesia:write(#presence{usr = USR, us = US,
priority = Priority})
end,
mnesia:transaction(F).
unset_presence(User, Server, Resource, Status) ->
USR = {User, Server, Resource},
F = fun() ->
mnesia:delete({presence, USR})
end,
mnesia:transaction(F),
ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
[User, Server, Resource, Status]).
get_user_present_resources(LUser, LServer) ->
US = {LUser, LServer},
case catch mnesia:dirty_index_read(presence, US, #presence.us) of
{'EXIT', _Reason} ->
[];
Rs ->
lists:map(fun(R) ->
{R#presence.priority, element(3, R#presence.usr)}
end, Rs)
end.
dirty_get_sessions_list() ->
mnesia:dirty_all_keys(session).
dirty_get_my_sessions_list() ->
mnesia:dirty_select(
session,
[{#session{pid = '$1', _ = '_'},
[{'==', {node, '$1'}, node()}],
['$_']}]).
get_vh_session_list(Server) ->
LServer = jlib:nameprep(Server),
mnesia:dirty_select(
session,
[{#session{usr = '$1', _ = '_'},
[{'==', {element, 2, '$1'}, LServer}],
['$1']}]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
process_iq(From, To, Packet) ->
@@ -429,15 +543,27 @@ process_iq(From, To, Packet) ->
ok
end.
register_iq_handler(Host, XMLNS, Module, Fun) ->
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun}.
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}.
unregister_iq_handler(Host, XMLNS) ->
ejabberd_sm ! {unregister_iq_handler, Host, XMLNS}.
ctl_process(_Val, ["connected-users"]) ->
USRs = dirty_get_sessions_list(),
NewLine = io_lib:format("~n", []),
SUSRs = lists:sort(USRs),
FUSRs = lists:map(fun({U, S, R}) -> [U, $@, S, $/, R, NewLine] end, SUSRs),
io:format("~s", [FUSRs]),
{stop, ?STATUS_SUCCESS};
ctl_process(_Val, ["connected-users-number"]) ->
N = length(dirty_get_sessions_list()),
io:format("~p~n", [N]),
{stop, ?STATUS_SUCCESS};
ctl_process(_Val, ["user-resources", User, Server]) ->
Resources = get_user_resources(User, Server),
NewLine = io_lib:format("~n", []),
SResources = lists:sort(Resources),
FResources = lists:map(fun(R) -> [R, NewLine] end, SResources),
io:format("~s", [FResources]),
{stop, ?STATUS_SUCCESS};
ctl_process(Val, _Args) ->
Val.
update_tables() ->
@@ -447,16 +573,16 @@ update_tables() ->
[ur, user, pid] ->
mnesia:delete_table(session);
[usr, us, pid] ->
mnesia:delete_table(session);
[sid, usr, us, priority] ->
ok;
{'EXIT', _} ->
ok
end,
case catch mnesia:table_info(presence, attributes) of
[ur, user, priority] ->
case lists:member(presence, mnesia:system_info(tables)) of
true ->
mnesia:delete_table(presence);
[usr, us, priority] ->
ok;
{'EXIT', _} ->
false ->
ok
end,
case lists:member(local_session, mnesia:system_info(tables)) of
+9
View File
@@ -68,6 +68,14 @@ init([]) ->
infinity,
supervisor,
[ejabberd_listener]},
ReceiverSupervisor =
{ejabberd_receiver_sup,
{ejabberd_tmp_sup, start_link,
[ejabberd_receiver_sup, ejabberd_receiver]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
C2SSupervisor =
{ejabberd_c2s_sup,
{ejabberd_tmp_sup, start_link, [ejabberd_c2s_sup, ejabberd_c2s]},
@@ -130,6 +138,7 @@ init([]) ->
SM,
S2S,
Local,
ReceiverSupervisor,
C2SSupervisor,
S2SInSupervisor,
S2SOutSupervisor,
+128
View File
@@ -0,0 +1,128 @@
%%%-------------------------------------------------------------------
%%% File : ejabberd_update.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Update ejabberd
%%% Created : 27 Jan 2006 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id: ejabberd_c2s.erl 492 2006-01-25 00:35:12Z alexey $
%%%-------------------------------------------------------------------
-module(ejabberd_update).
-author('alexey@sevcom.net').
%% API
-export([update/0, update_info/0]).
%%====================================================================
%% API
%%====================================================================
update() ->
case update_info() of
{ok, Dir, _UpdatedBeams, _Script, LowLevelScript, _Check} ->
Eval =
release_handler_1:eval_script(
LowLevelScript, [],
[{ejabberd, "", filename:join(Dir, "..")}]),
io:format("eval: ~p~n", [Eval]),
Eval;
{error, Reason} ->
{error, Reason}
end.
update_info() ->
Dir = filename:dirname(code:which(ejabberd)),
case file:list_dir(Dir) of
{ok, Files} ->
Beams = [list_to_atom(filename:rootname(FN)) ||
FN <- Files, lists:suffix(".beam", FN)],
UpdatedBeams =
lists:filter(
fun(Module) ->
{ok, {Module, [NewVsn]}} =
beam_lib:version(code:which(Module)),
case code:is_loaded(Module) of
{file, _} ->
Attrs = Module:module_info(attributes),
{value, {vsn, [CurVsn]}} =
lists:keysearch(vsn, 1, Attrs),
NewVsn /= CurVsn;
false ->
false
end
end, Beams),
io:format("beam files: ~p~n", [UpdatedBeams]),
Script = make_script(UpdatedBeams),
io:format("script: ~p~n", [Script]),
LowLevelScript = make_low_level_script(UpdatedBeams, Script),
io:format("low level script: ~p~n", [LowLevelScript]),
Check =
release_handler_1:check_script(
LowLevelScript,
[{ejabberd, "", filename:join(Dir, "..")}]),
io:format("check: ~p~n", [Check]),
{ok, Dir, UpdatedBeams, Script, LowLevelScript, Check};
{error, Reason} ->
{error, Reason}
end.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
%% From systools.hrl
-record(application,
{name, %% Name of the application, atom().
type = permanent, %% Application start type, atom().
vsn = "", %% Version of the application, string().
id = "", %% Id of the application, string().
description = "", %% Description of application, string().
modules = [], %% [Module | {Module,Vsn}] of modules
%% incorporated in the application,
%% Module = atom(), Vsn = string().
uses = [], %% [Application] list of applications required
%% by the application, Application = atom().
includes = [], %% [Application] list of applications included
%% by the application, Application = atom().
regs = [], %% [RegNames] a list of registered process
%% names used by the application, RegNames =
%% atom().
env = [], %% [{Key,Value}] environment variable of
%% application, Key = Value = term().
maxT = infinity, %% Max time an application may exist,
%% integer() | infinity.
maxP = infinity, %% Max number of processes in an application,
%% integer() | infinity.
mod = [], %% [] | {Mod, StartArgs}, Mod= atom(),
%% StartArgs = list().
start_phases = [], %% [] | {Phase, PhaseArgs}, Phase = atom(),
%% PhaseArgs = list().
dir = "" %% The directory where the .app file was
%% found (internal use).
}).
make_script(UpdatedBeams) ->
lists:map(
fun(Module) ->
{ok, {Module, [{attributes, NewAttrs}]}} =
beam_lib:chunks(code:which(Module), [attributes]),
CurAttrs = Module:module_info(attributes),
case lists:keysearch(update_info, 1, NewAttrs) of
{value, {_, [{update, _}]}} ->
case lists:keysearch(update_info, 1, CurAttrs) of
{value, {_, [{update, Extra}]}} ->
{update, Module, {advanced, Extra}};
false ->
{update, Module, {advanced, 0}}
end;
false ->
{load_module, Module}
end
end, UpdatedBeams).
make_low_level_script(UpdatedBeams, Script) ->
EJDApp = #application{name = ejabberd,
modules = UpdatedBeams},
{ok, LowLevelScript} =
systools_rc:translate_scripts([Script], [EJDApp], [EJDApp]),
LowLevelScript.
+38
View File
@@ -0,0 +1,38 @@
# $Id$
CC = @CC@
CFLAGS = @CFLAGS@ @ZLIB_CFLAGS@ @ERLANG_CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@ @ZLIB_LIBS@ @ERLANG_LIBS@
SUBDIRS =
ERLSHLIBS = ../ejabberd_zlib_drv.so
OUTDIR = ..
EFLAGS = -I .. -pz ..
OBJS = \
$(OUTDIR)/ejabberd_zlib.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 $(CPPFLAGS) $(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)\ejabberd_zlib.beam
SOURCE = ejabberd_zlib_drv.c
OBJECT = ejabberd_zlib_drv.o
DLL = $(OUTDIR)\ejabberd_zlib_drv.dll
ALL : $(DLL) $(OBJS)
CLEAN :
-@erase $(DLL)
-@erase $(OUTDIR)\ejabberd_zlib_drv.exp
-@erase $(OUTDIR)\ejabberd_zlib_drv.lib
-@erase $(OBJECT)
-@erase $(OBJS)
$(OUTDIR)\ejabberd_zlib.beam : ejabberd_zlib.erl
erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_zlib.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"$(ZLIB_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" "$(ZLIB_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)
+134
View File
@@ -0,0 +1,134 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_zlib.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Interface to zlib
%%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_zlib).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
-export([start/0, start_link/0,
enable_zlib/2, disable_zlib/1,
send/2,
recv/2, recv/3, recv_data/2,
setopts/2,
controlling_process/2,
close/1]).
%% Internal exports, call-back functions.
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
code_change/3,
terminate/2]).
-define(DEFLATE, 1).
-define(INFLATE, 2).
-record(zlibsock, {sockmod, socket, zlibport}).
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
ok -> ok;
{error, already_loaded} -> ok
end,
Port = open_port({spawn, ejabberd_zlib_drv}, [binary]),
{ok, Port}.
%%% --------------------------------------------------------
%%% The call-back functions.
%%% --------------------------------------------------------
handle_call(_, _, State) ->
{noreply, State}.
handle_cast(_, State) ->
{noreply, State}.
handle_info({'EXIT', Port, Reason}, Port) ->
{stop, {port_died, Reason}, Port};
handle_info({'EXIT', _Pid, _Reason}, Port) ->
{noreply, Port};
handle_info(_, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, Port) ->
Port ! {self, close},
ok.
enable_zlib(SockMod, Socket) ->
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
ok -> ok;
{error, already_loaded} -> ok
end,
Port = open_port({spawn, ejabberd_zlib_drv}, [binary]),
{ok, #zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}}.
disable_zlib(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
port_close(Port),
{SockMod, Socket}.
recv(Socket, Length) ->
recv(Socket, Length, infinity).
recv(#zlibsock{sockmod = SockMod, socket = Socket} = ZlibSock,
Length, Timeout) ->
case SockMod:recv(Socket, Length, Timeout) of
{ok, Packet} ->
recv_data(ZlibSock, Packet);
{error, _Reason} = Error ->
Error
end.
recv_data(#zlibsock{zlibport = Port} = _ZlibSock, Packet) ->
case port_control(Port, ?INFLATE, Packet) of
<<0, In/binary>> ->
{ok, In};
<<1, Error/binary>> ->
{error, binary_to_list(Error)}
end.
send(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port},
Packet) ->
case port_control(Port, ?DEFLATE, Packet) of
<<0, Out/binary>> ->
SockMod:send(Socket, Out);
<<1, Error/binary>> ->
{error, binary_to_list(Error)}
end.
setopts(#zlibsock{sockmod = SockMod, socket = Socket}, Opts) ->
case SockMod of
gen_tcp ->
inet:setopts(Socket, Opts);
_ ->
SockMod:setopts(Socket, Opts)
end.
controlling_process(#zlibsock{sockmod = SockMod, socket = Socket}, Pid) ->
SockMod:controlling_process(Socket, Pid).
close(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
SockMod:close(Socket),
port_close(Port).
+171
View File
@@ -0,0 +1,171 @@
/* $Id$ */
#include <stdio.h>
#include <string.h>
#include <erl_driver.h>
#include <zlib.h>
#define BUF_SIZE 1024
typedef struct {
ErlDrvPort port;
z_stream *d_stream;
z_stream *i_stream;
} ejabberd_zlib_data;
static ErlDrvData ejabberd_zlib_drv_start(ErlDrvPort port, char *buff)
{
ejabberd_zlib_data *d =
(ejabberd_zlib_data *)driver_alloc(sizeof(ejabberd_zlib_data));
d->port = port;
d->d_stream = (z_stream *)malloc(sizeof(z_stream));
d->d_stream->zalloc = (alloc_func)0;
d->d_stream->zfree = (free_func)0;
d->d_stream->opaque = (voidpf)0;
deflateInit(d->d_stream, Z_DEFAULT_COMPRESSION);
d->i_stream = (z_stream *)malloc(sizeof(z_stream));
d->i_stream->zalloc = (alloc_func)0;
d->i_stream->zfree = (free_func)0;
d->i_stream->opaque = (voidpf)0;
inflateInit(d->i_stream);
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
return (ErlDrvData)d;
}
static void ejabberd_zlib_drv_stop(ErlDrvData handle)
{
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
deflateEnd(d->d_stream);
free(d->d_stream);
inflateEnd(d->i_stream);
free(d->i_stream);
driver_free((char *)handle);
}
#define DEFLATE 1
#define INFLATE 2
#define die_unless(cond, errstr) \
if (!(cond)) \
{ \
rlen = strlen(errstr) + 1; \
b = driver_realloc_binary(b, rlen); \
b->orig_bytes[0] = 1; \
strncpy(b->orig_bytes + 1, errstr, rlen - 1); \
*rbuf = (char *)b; \
return rlen; \
}
static int ejabberd_zlib_drv_control(ErlDrvData handle,
unsigned int command,
char *buf, int len,
char **rbuf, int rlen)
{
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
int err;
int size;
ErlDrvBinary *b;
switch (command)
{
case DEFLATE:
size = BUF_SIZE + 1;
rlen = 1;
b = driver_alloc_binary(size);
b->orig_bytes[0] = 0;
d->d_stream->next_in = buf;
d->d_stream->avail_in = len;
d->d_stream->avail_out = 0;
err = Z_OK;
while (err == Z_OK && d->d_stream->avail_out == 0)
{
d->d_stream->next_out = b->orig_bytes + rlen;
d->d_stream->avail_out = BUF_SIZE;
err = deflate(d->d_stream, Z_SYNC_FLUSH);
die_unless((err == Z_OK) || (err == Z_STREAM_END),
"Deflate error");
rlen += (BUF_SIZE - d->d_stream->avail_out);
size += (BUF_SIZE - d->d_stream->avail_out);
b = driver_realloc_binary(b, size);
}
b = driver_realloc_binary(b, rlen);
*rbuf = (char *)b;
return rlen;
case INFLATE:
size = BUF_SIZE + 1;
rlen = 1;
b = driver_alloc_binary(size);
b->orig_bytes[0] = 0;
if (len > 0) {
d->i_stream->next_in = buf;
d->i_stream->avail_in = len;
d->i_stream->avail_out = 0;
err = Z_OK;
while (err == Z_OK && d->i_stream->avail_out == 0)
{
d->i_stream->next_out = b->orig_bytes + rlen;
d->i_stream->avail_out = BUF_SIZE;
err = inflate(d->i_stream, Z_SYNC_FLUSH);
die_unless((err == Z_OK) || (err == Z_STREAM_END),
"Inflate error");
rlen += (BUF_SIZE - d->i_stream->avail_out);
size += (BUF_SIZE - d->i_stream->avail_out);
b = driver_realloc_binary(b, size);
}
}
b = driver_realloc_binary(b, rlen);
*rbuf = (char *)b;
return rlen;
}
b = driver_alloc_binary(1);
b->orig_bytes[0] = 0;
*rbuf = (char *)b;
return 1;
}
ErlDrvEntry ejabberd_zlib_driver_entry = {
NULL, /* F_PTR init, N/A */
ejabberd_zlib_drv_start, /* L_PTR start, called when port is opened */
ejabberd_zlib_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 */
"ejabberd_zlib_drv", /* char *driver_name, the argument to open_port */
NULL, /* F_PTR finish, called when unloaded */
NULL, /* handle */
ejabberd_zlib_drv_control, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL /* F_PTR outputv, reserved */
};
DRIVER_INIT(ejabberd_zlib_drv) /* must match name in driver_entry */
{
return &ejabberd_zlib_driver_entry;
}
+1 -1
View File
@@ -164,7 +164,7 @@ void *erlXML_CharacterDataHandler(expat_data *d,
ei_x_encode_list_header(&event_buf, 1);
ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_long(&event_buf, XML_CDATA);
ei_x_encode_string_len_fixed(&event_buf, s, len);
ei_x_encode_binary(&event_buf, s, len);
return NULL;
}
+114 -22
View File
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : gen_iq_handler.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose :
%%% Purpose : IQ handler support
%%% Created : 22 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
@@ -10,19 +10,35 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-export([start/0,
start_link/3,
-behaviour(gen_server).
%% API
-export([start_link/3,
add_iq_handler/6,
remove_iq_handler/3,
stop_iq_handler/3,
handle/7,
process_iq/6,
queue_init/3]).
process_iq/6]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
start() ->
ok.
-record(state, {host,
module,
function}).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Host, Module, Function) ->
gen_server:start_link(?MODULE, [Host, Module, Function], []).
add_iq_handler(Component, Host, NS, Module, Function, Type) ->
case Type of
@@ -33,6 +49,17 @@ add_iq_handler(Component, Host, NS, Module, Function, Type) ->
[Host, Module, Function]),
Component:register_iq_handler(Host, NS, Module, Function,
{one_queue, Pid});
{queues, N} ->
Pids =
lists:map(
fun(_) ->
{ok, Pid} = supervisor:start_child(
ejabberd_iq_sup,
[Host, Module, Function]),
Pid
end, lists:seq(1, N)),
Component:register_iq_handler(Host, NS, Module, Function,
{queues, Pids});
parallel ->
Component:register_iq_handler(Host, NS, Module, Function, parallel)
end.
@@ -40,10 +67,14 @@ add_iq_handler(Component, Host, NS, Module, Function, Type) ->
remove_iq_handler(Component, Host, NS) ->
Component:unregister_iq_handler(Host, NS).
stop_iq_handler(Module, Function, Opts) ->
stop_iq_handler(_Module, _Function, Opts) ->
case Opts of
{one_queue, Pid} ->
exit(Pid, kill);
gen_server:call(Pid, stop);
{queues, Pids} ->
lists:foreach(fun(Pid) ->
catch gen_server:call(Pid, stop)
end, Pids);
_ ->
ok
end.
@@ -54,6 +85,9 @@ handle(Host, Module, Function, Opts, From, To, IQ) ->
process_iq(Host, Module, Function, From, To, IQ);
{one_queue, Pid} ->
Pid ! {process_iq, From, To, IQ};
{queues, Pids} ->
Pid = lists:nth(erlang:phash(now(), length(Pids)), Pids),
Pid ! {process_iq, From, To, IQ};
parallel ->
spawn(?MODULE, process_iq, [Host, Module, Function, From, To, IQ]);
_ ->
@@ -75,18 +109,76 @@ process_iq(_Host, Module, Function, From, To, IQ) ->
end
end.
start_link(Host, Module, Function) ->
{ok, proc_lib:spawn_link(?MODULE, queue_init, [Host, Module, Function])}.
%%====================================================================
%% gen_server callbacks
%%====================================================================
queue_init(Host, Module, Function) ->
queue_loop(Host, Module, Function).
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Module, Function]) ->
{ok, #state{host = Host,
module = Module,
function = Function}}.
% TODO: use gen_event
queue_loop(Host, Module, Function) ->
receive
{process_iq, From, To, IQ} ->
process_iq(Host, Module, Function, From, To, IQ),
queue_loop(Host, Module, Function);
_ ->
queue_loop(Host, Module, Function)
end.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(stop, _From, State) ->
Reply = ok,
{stop, normal, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({process_iq, From, To, IQ},
#state{host = Host,
module = Module,
function = Function} = State) ->
process_iq(Host, Module, Function, From, To, IQ),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+5 -1
View File
@@ -19,7 +19,8 @@
loaded_modules/1,
loaded_modules_with_opts/1,
get_hosts/2,
get_module_proc/2]).
get_module_proc/2,
is_loaded/2]).
-export([behaviour_info/1]).
@@ -144,3 +145,6 @@ get_hosts(Opts, Prefix) ->
get_module_proc(Host, Base) ->
list_to_atom(atom_to_list(Base) ++ "_" ++ Host).
is_loaded(Host, Module) ->
ets:member(ejabberd_modules, {Module, Host}).
+13 -4
View File
@@ -134,10 +134,19 @@ xdb_data(User, Server, {xmlelement, _Name, Attrs, _Els} = El) ->
end,
ok;
?NS_VCARD ->
catch mod_vcard:process_sm_iq(
From,
jlib:make_jid("", Server, ""),
#iq{type = set, xmlns = ?NS_VCARD, sub_el = El}),
case lists:member(mod_vcard_odbc,
gen_mod:loaded_modules(LServer)) of
true ->
catch mod_vcard_odbc:process_sm_iq(
From,
jlib:make_jid("", Server, ""),
#iq{type = set, xmlns = ?NS_VCARD, sub_el = El});
false ->
catch mod_vcard:process_sm_iq(
From,
jlib:make_jid("", Server, ""),
#iq{type = set, xmlns = ?NS_VCARD, sub_el = El})
end,
ok;
"jabber:x:offline" ->
process_offline(Server, From, El),
+4
View File
@@ -33,6 +33,7 @@
-define(NS_PUBSUB_EVENT, "http://jabber.org/protocol/pubsub#event").
-define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
-define(NS_PUBSUB_NMI, "http://jabber.org/protocol/pubsub#node-meta-info").
-define(NS_COMMANDS, "http://jabber.org/protocol/commands").
-define(NS_EJABBERD_CONFIG, "ejabberd:config").
@@ -48,6 +49,9 @@
-define(NS_FEATURE_IQAUTH, "http://jabber.org/features/iq-auth").
-define(NS_FEATURE_IQREGISTER, "http://jabber.org/features/iq-register").
-define(NS_FEATURE_COMPRESS, "http://jabber.org/features/compress").
-define(NS_COMPRESS, "http://jabber.org/protocol/compress").
% TODO: remove "code" attribute (currently it used for backward-compatibility)
-define(STANZA_ERROR(Code, Type, Condition),
+250
View File
@@ -0,0 +1,250 @@
%%%----------------------------------------------------------------------
%%% File : mod_adhoc.erl
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
%%% Purpose : Handle incoming ad-doc requests (JEP-0050)
%%% Created : 15 Nov 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(mod_adhoc).
-author('henoch@dtek.chalmers.se').
-vsn('$Revision$ ').
-behaviour(gen_mod).
-export([start/2,
stop/1,
process_local_iq/3,
process_sm_iq/3,
get_local_commands/5,
get_local_identity/5,
get_local_features/5,
get_sm_commands/5,
get_sm_identity/5,
get_sm_features/5,
ping_item/4,
ping_command/4]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS,
?MODULE, process_local_iq, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS,
?MODULE, process_sm_iq, IQDisc),
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 99),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 99),
ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_local_commands, 99),
ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 99),
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 99),
ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_commands, 99),
ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, ping_item, 100),
ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, ping_command, 100).
stop(Host) ->
ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, ping_command, 100),
ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, ping_item, 100),
ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_commands, 99),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 99),
ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 99),
ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_commands, 99),
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 99),
ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 99),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS),
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS).
%-------------------------------------------------------------------------
get_local_commands(Acc, _From, #jid{server = Server, lserver = LServer} = _To, "", Lang) ->
Display = gen_mod:get_module_opt(LServer, ?MODULE, report_commands_node, false),
case Display of
false ->
Acc;
_ ->
Items = case Acc of
{result, I} -> I;
_ -> []
end,
Nodes = [{xmlelement,
"item",
[{"jid", Server},
{"node", ?NS_COMMANDS},
{"name", translate:translate(Lang, "Commands")}],
[]}],
{result, Items ++ Nodes}
end;
get_local_commands(_Acc, From, #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) ->
ejabberd_hooks:run_fold(adhoc_local_items, LServer, {result, []}, [From, To, Lang]);
get_local_commands(_Acc, _From, _To, "ping", _Lang) ->
{result, []};
get_local_commands(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
get_sm_commands(Acc, _From, #jid{lserver = LServer} = To, "", Lang) ->
Display = gen_mod:get_module_opt(LServer, ?MODULE, report_commands_node, false),
case Display of
false ->
Acc;
_ ->
Items = case Acc of
{result, I} -> I;
_ -> []
end,
Nodes = [{xmlelement,
"item",
[{"jid", jlib:jid_to_string(To)},
{"node", ?NS_COMMANDS},
{"name", translate:translate(Lang, "Commands")}],
[]}],
{result, Items ++ Nodes}
end;
get_sm_commands(_Acc, From, #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) ->
ejabberd_hooks:run_fold(adhoc_sm_items, LServer, {result, []}, [From, To, Lang]);
get_sm_commands(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
%% On disco info request to the ad-hoc node, return automation/command-list.
get_local_identity(Acc, _From, _To, ?NS_COMMANDS, Lang) ->
[{xmlelement, "identity",
[{"category", "automation"},
{"type", "command-list"},
{"name", translate:translate(Lang, "Commands")}], []} | Acc];
get_local_identity(Acc, _From, _To, "ping", Lang) ->
[{xmlelement, "identity",
[{"category", "automation"},
{"type", "command-node"},
{"name", translate:translate(Lang, "Ping")}], []} | Acc];
get_local_identity(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
%% On disco info request to the ad-hoc node, return automation/command-list.
get_sm_identity(Acc, _From, _To, ?NS_COMMANDS, Lang) ->
[{xmlelement, "identity",
[{"category", "automation"},
{"type", "command-list"},
{"name", translate:translate(Lang, "Commands")}], []} | Acc];
get_sm_identity(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
get_local_features(Acc, _From, _To, "", _Lang) ->
Feats = case Acc of
{result, I} -> I;
_ -> []
end,
{result, Feats ++ [?NS_COMMANDS]};
get_local_features(_Acc, _From, _To, ?NS_COMMANDS, _Lang) ->
%% override all lesser features...
{result, []};
get_local_features(_Acc, _From, _To, "ping", _Lang) ->
%% override all lesser features...
{result, [?NS_COMMANDS]};
get_local_features(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
get_sm_features(Acc, _From, _To, "", _Lang) ->
Feats = case Acc of
{result, I} -> I;
_ -> []
end,
{result, Feats ++ [?NS_COMMANDS]};
get_sm_features(_Acc, _From, _To, ?NS_COMMANDS, _Lang) ->
%% override all lesser features...
{result, []};
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
process_local_iq(From, To, IQ) ->
process_adhoc_request(From, To, IQ, adhoc_local_commands).
process_sm_iq(From, To, IQ) ->
process_adhoc_request(From, To, IQ, adhoc_sm_commands).
process_adhoc_request(From, To, #iq{sub_el = SubEl} = IQ, Hook) ->
?DEBUG("About to parse ~p...", [IQ]),
case adhoc:parse_request(IQ) of
{error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]};
#adhoc_request{} = AdhocRequest ->
Host = To#jid.lserver,
case ejabberd_hooks:run_fold(Hook, Host, empty,
[From, To, AdhocRequest]) of
ignore ->
ignore;
empty ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]};
{error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]};
Command ->
IQ#iq{type = result, sub_el = [Command]}
end
end.
ping_item(Acc, _From, #jid{server = Server} = _To, Lang) ->
Items = case Acc of
{result, I} ->
I;
_ ->
[]
end,
Nodes = [{xmlelement, "item",
[{"jid", Server},
{"node", "ping"},
{"name", translate:translate(Lang, "Ping")}],
[]}],
{result, Items ++ Nodes}.
ping_command(_Acc, _From, _To,
#adhoc_request{lang = Lang,
node = "ping",
sessionid = _Sessionid,
action = Action} = Request) ->
if
Action == ""; Action == "execute" ->
adhoc:produce_response(
Request,
#adhoc_response{status = completed,
notes = [{"info", translate:translate(
Lang,
"Pong")}]});
true ->
{error, ?ERR_BAD_REQUEST}
end;
ping_command(Acc, _From, _To, _Request) ->
Acc.
+436 -7
View File
@@ -15,10 +15,16 @@
init/0,
stop/1,
announce/3,
send_motd/1]).
send_motd/1,
disco_identity/5,
disco_features/5,
disco_items/5,
announce_commands/4,
announce_items/4]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").
-record(motd, {server, packet}).
-record(motd_users, {us, dummy = []}).
@@ -33,6 +39,11 @@ start(Host, _Opts) ->
update_tables(),
ejabberd_hooks:add(local_send_to_resource_hook, Host,
?MODULE, announce, 50),
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 50),
ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, announce_items, 50),
ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, announce_commands, 50),
ejabberd_hooks:add(user_available_hook, Host,
?MODULE, send_motd, 50),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
@@ -66,6 +77,11 @@ loop() ->
end.
stop(Host) ->
ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, announce_commands, 50),
ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, announce_items, 50),
ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 50),
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50),
ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 50),
ejabberd_hooks:delete(local_send_to_resource_hook, Host,
?MODULE, announce, 50),
ejabberd_hooks:delete(sm_register_connection_hook, Host,
@@ -74,6 +90,7 @@ stop(Host) ->
exit(whereis(Proc), stop),
{wait, Proc}.
% Announcing via messages to a custom resource
announce(From, To, Packet) ->
case To of
#jid{luser = "", lresource = Res} ->
@@ -105,12 +122,422 @@ announce(From, To, Packet) ->
ok
end.
%-------------------------------------------------------------------------
% Announcing via ad-hoc commands
-define(INFO_COMMAND(Lang, Node),
[{xmlelement, "identity",
[{"category", "automation"},
{"type", "command-node"},
{"name", get_title(Lang, Node)}], []}]).
disco_identity(Acc, _From, _To, Node, Lang) ->
case Node of
"announce/all" ->
?INFO_COMMAND(Lang, Node);
"announce/all-hosts/online" ->
?INFO_COMMAND(Lang, Node);
"announce/online" ->
?INFO_COMMAND(Lang, Node);
"announce/motd" ->
?INFO_COMMAND(Lang, Node);
"announce/motd/delete" ->
?INFO_COMMAND(Lang, Node);
"announce/motd/update" ->
?INFO_COMMAND(Lang, Node);
_ ->
Acc
end.
%-------------------------------------------------------------------------
-define(INFO_RESULT(Allow, Feats),
case Allow of
deny ->
{error, ?ERR_FORBIDDEN};
allow ->
{result, Feats}
end).
disco_features(Acc, From, #jid{lserver = LServer} = _To,
"announce", _Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
case {acl:match_rule(LServer, Access1, From),
acl:match_rule(global, Access2, From)} of
{deny, deny} ->
{error, ?ERR_FORBIDDEN};
_ ->
{result, []}
end
end;
disco_features(Acc, From, #jid{lserver = LServer} = _To,
"announce/all-hosts/online", _Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
Allow = acl:match_rule(global, Access, From),
?INFO_RESULT(Allow, [?NS_COMMANDS])
end;
disco_features(Acc, From, #jid{lserver = LServer} = _To,
Node, _Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Allow = acl:match_rule(LServer, Access, From),
case Node of
"announce/all" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
"announce/online" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
"announce/motd" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
"announce/motd/delete" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
"announce/motd/update" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
_ ->
Acc
end
end.
%-------------------------------------------------------------------------
-define(NODE_TO_ITEM(Lang, Server, Node),
{xmlelement, "item",
[{"jid", Server},
{"node", Node},
{"name", get_title(Lang, Node)}],
[]}).
-define(ITEMS_RESULT(Allow, Items),
case Allow of
deny ->
{error, ?ERR_FORBIDDEN};
allow ->
{result, Items}
end).
disco_items(Acc, From, #jid{lserver = LServer, server = Server} = _To,
"", Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
case {acl:match_rule(LServer, Access1, From),
acl:match_rule(global, Access2, From)} of
{deny, deny} ->
Acc;
_ ->
Items = case Acc of
{result, I} -> I;
_ -> []
end,
Nodes = [?NODE_TO_ITEM(Lang, Server, "announce")],
{result, Items ++ Nodes}
end
end;
disco_items(Acc, From, #jid{lserver = LServer} = To, "announce", Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
announce_items(Acc, From, To, Lang)
end;
disco_items(Acc, From, #jid{lserver = LServer} = _To,
"announce/all-hosts/online", _Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
Allow = acl:match_rule(global, Access, From),
?ITEMS_RESULT(Allow, [])
end;
disco_items(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Allow = acl:match_rule(LServer, Access, From),
case Node of
"announce/all" ->
?ITEMS_RESULT(Allow, []);
"announce/online" ->
?ITEMS_RESULT(Allow, []);
"announce/motd" ->
?ITEMS_RESULT(Allow, []);
"announce/motd/delete" ->
?ITEMS_RESULT(Allow, []);
"announce/motd/update" ->
?ITEMS_RESULT(Allow, []);
_ ->
Acc
end
end.
%-------------------------------------------------------------------------
announce_items(Acc, From, #jid{lserver = LServer, server = Server} = _To, Lang) ->
Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Nodes1 = case acl:match_rule(LServer, Access1, From) of
allow ->
[?NODE_TO_ITEM(Lang, Server, "announce/all"),
?NODE_TO_ITEM(Lang, Server, "announce/online"),
?NODE_TO_ITEM(Lang, Server, "announce/motd"),
?NODE_TO_ITEM(Lang, Server, "announce/motd/delete"),
?NODE_TO_ITEM(Lang, Server, "announce/motd/update")];
deny ->
[]
end,
Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
Nodes2 = case acl:match_rule(global, Access2, From) of
allow ->
[?NODE_TO_ITEM(Lang, Server, "announce/all-hosts/online")];
deny ->
[]
end,
case {Nodes1, Nodes2} of
{[], []} ->
Acc;
_ ->
Items = case Acc of
{result, I} -> I;
_ -> []
end,
{result, Items ++ Nodes1 ++ Nodes2}
end.
%-------------------------------------------------------------------------
-define(COMMANDS_RESULT(Allow, From, To, Request),
case Allow of
deny ->
{error, ?ERR_FORBIDDEN};
allow ->
announce_commands(From, To, Request)
end).
announce_commands(_Acc, From, To,
#adhoc_request{
node = "announce/all-hosts/online"} = Request) ->
Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
Allow = acl:match_rule(global, Access, From),
?COMMANDS_RESULT(Allow, From, To, Request);
announce_commands(Acc, From, #jid{lserver = LServer} = To,
#adhoc_request{node = Node} = Request) ->
Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Allow = acl:match_rule(LServer, Access, From),
case Node of
"announce/all" ->
?COMMANDS_RESULT(Allow, From, To, Request);
"announce/online" ->
?COMMANDS_RESULT(Allow, From, To, Request);
"announce/motd" ->
?COMMANDS_RESULT(Allow, From, To, Request);
"announce/motd/delete" ->
?COMMANDS_RESULT(Allow, From, To, Request);
"announce/motd/update" ->
?COMMANDS_RESULT(Allow, From, To, Request);
_ ->
Acc
end.
%-------------------------------------------------------------------------
announce_commands(From, To,
#adhoc_request{lang = Lang,
node = Node,
action = Action,
xdata = XData} = Request) ->
%% If the "action" attribute is not present, it is
%% understood as "execute". If there was no <actions/>
%% element in the first response (which there isn't in our
%% case), "execute" and "complete" are equivalent.
ActionIsExecute = lists:member(Action,
["", "execute", "complete"]),
if Action == "cancel" ->
%% User cancels request
adhoc:produce_response(Request,
#adhoc_response{status = canceled});
XData == false, ActionIsExecute ->
%% User requests form
adhoc:produce_response(
Request,
#adhoc_response{status = executing,
elements = [generate_adhoc_form(Lang, Node)]});
XData /= false, ActionIsExecute ->
%% User returns form.
case jlib:parse_xdata_submit(XData) of
invalid ->
{error, ?ERR_BAD_REQUEST};
Fields ->
handle_adhoc_form(From, To, Request, Fields)
end;
true ->
{error, ?ERR_BAD_REQUEST}
end.
generate_adhoc_form(Lang, Node) ->
{xmlelement, "x",
[{"xmlns", ?NS_XDATA},
{"type", "form"}],
[{xmlelement, "title", [], [{xmlcdata, get_title(Lang, Node)}]}]
++
if Node == "announce/motd/delete" ->
[{xmlelement, "field",
[{"var", "confirm"},
{"type", "boolean"},
{"label", translate:translate(Lang, "Really delete message of the day?")}],
[{xmlelement, "value",
[],
[{xmlcdata, "true"}]}]}];
true ->
[{xmlelement, "field",
[{"var", "subject"},
{"type", "text-single"},
{"label", translate:translate(Lang, "Subject")}],
[]},
{xmlelement, "field",
[{"var", "body"},
{"type", "text-multi"},
{"label", translate:translate(Lang, "Message body")}],
[]}]
end}.
join_lines([]) ->
[];
join_lines(Lines) ->
join_lines(Lines, []).
join_lines([Line|Lines], Acc) ->
join_lines(Lines, ["\n",Line|Acc]);
join_lines([], Acc) ->
%% Remove last newline
lists:flatten(lists:reverse(tl(Acc))).
handle_adhoc_form(From, #jid{lserver = LServer} = To,
#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID},
Fields) ->
Confirm = case lists:keysearch("confirm", 1, Fields) of
{value, {"confirm", ["true"]}} ->
true;
{value, {"confirm", ["1"]}} ->
true;
_ ->
false
end,
Subject = case lists:keysearch("subject", 1, Fields) of
{value, {"subject", SubjectLines}} ->
%% There really shouldn't be more than one
%% subject line, but can we stop them?
join_lines(SubjectLines);
_ ->
[]
end,
Body = case lists:keysearch("body", 1, Fields) of
{value, {"body", BodyLines}} ->
join_lines(BodyLines);
_ ->
[]
end,
Response = #adhoc_response{lang = Lang,
node = Node,
sessionid = SessionID,
status = completed},
Packet = {xmlelement, "message", [{"type", "normal"}],
if Subject /= [] ->
[{xmlelement, "subject", [],
[{xmlcdata, Subject}]}];
true ->
[]
end ++
if Body /= [] ->
[{xmlelement, "body", [],
[{xmlcdata, Body}]}];
true ->
[]
end},
Proc = gen_mod:get_module_proc(LServer, ?PROCNAME),
case {Node, Body} of
{"announce/motd/delete", _} ->
if Confirm ->
Proc ! {announce_motd_delete, From, To, Packet},
adhoc:produce_response(Response);
true ->
adhoc:produce_response(Response)
end;
{_, []} ->
%% An announce message with no body is definitely an operator error.
%% Throw an error and give him/her a chance to send message again.
{error, ?ERRT_NOT_ACCEPTABLE(
Lang,
"No body provided for announce message")};
%% Now send the packet to ?PROCNAME.
%% We don't use direct announce_* functions because it
%% leads to large delay in response and <iq/> queries processing
{"announce/all", _} ->
Proc ! {announce_all, From, To, Packet},
adhoc:produce_response(Response);
{"announce/online", _} ->
Proc ! {announce_online, From, To, Packet},
adhoc:produce_response(Response);
{"announce/all-hosts/online", _} ->
Proc ! {announce_all_hosts_online, From, To, Packet},
adhoc:produce_response(Response);
{"announce/motd", _} ->
Proc ! {announce_motd, From, To, Packet},
adhoc:produce_response(Response);
{"announce/motd/update", _} ->
Proc ! {announce_motd_update, From, To, Packet},
adhoc:produce_response(Response);
_ ->
%% This can't happen, as we haven't registered any other
%% command nodes.
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
get_title(Lang, "announce") ->
translate:translate(Lang, "Announcements");
get_title(Lang, "announce/all") ->
translate:translate(Lang, "Send announcement to all users");
get_title(Lang, "announce/online") ->
translate:translate(Lang, "Send announcement to all online users");
get_title(Lang, "announce/all-hosts/online") ->
translate:translate(Lang, "Send announcement to all online users on all hosts");
get_title(Lang, "announce/motd") ->
translate:translate(Lang, "Set message of the day and send to online users");
get_title(Lang, "announce/motd/update") ->
translate:translate(Lang, "Update message of the day (don't send)");
get_title(Lang, "announce/motd/delete") ->
translate:translate(Lang, "Delete message of the day").
%-------------------------------------------------------------------------
announce_all(From, To, Packet) ->
Host = To#jid.lserver,
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
Local = jlib:make_jid("", To#jid.server, ""),
@@ -126,7 +553,7 @@ announce_online(From, To, Packet) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
announce_online1(ejabberd_sm:get_vh_session_list(Host),
@@ -138,7 +565,7 @@ announce_all_hosts_online(From, To, Packet) ->
Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
case acl:match_rule(global, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
announce_online1(ejabberd_sm:dirty_get_sessions_list(),
@@ -159,7 +586,7 @@ announce_motd(From, To, Packet) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd_update(To#jid.lserver, Packet),
@@ -179,7 +606,7 @@ announce_motd_update(From, To, Packet) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd_update(Host, Packet)
@@ -197,7 +624,7 @@ announce_motd_delete(From, To, Packet) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd_delete(Host)
@@ -237,6 +664,7 @@ send_motd(#jid{luser = LUser, lserver = LServer} = JID) ->
ok
end.
%-------------------------------------------------------------------------
update_tables() ->
update_motd_table(),
@@ -326,3 +754,4 @@ update_motd_users_table() ->
?INFO_MSG("Recreating motd_users table", []),
mnesia:transform_table(motd_users, ignore, Fields)
end.
+468 -296
View File
File diff suppressed because it is too large Load Diff
+15 -26
View File
@@ -113,8 +113,7 @@ process_local_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} =
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
SNode = xml:get_tag_attr_s("node", SubEl),
Node = string:tokens(SNode, "/"),
Node = xml:get_tag_attr_s("node", SubEl),
Host = To#jid.lserver,
case ejabberd_hooks:run_fold(disco_local_items,
@@ -123,8 +122,8 @@ process_local_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} =
[From, To, Node, Lang]) of
{result, Items} ->
ANode = case Node of
[] -> [];
_ -> [{"node", SNode}]
"" -> [];
_ -> [{"node", Node}]
end,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@@ -144,8 +143,7 @@ process_local_iq_info(From, To, #iq{type = Type, lang = Lang,
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
Host = To#jid.lserver,
SNode = xml:get_tag_attr_s("node", SubEl),
Node = string:tokens(SNode, "/"),
Node = xml:get_tag_attr_s("node", SubEl),
Identity = ejabberd_hooks:run_fold(disco_local_identity,
Host,
[],
@@ -156,8 +154,8 @@ process_local_iq_info(From, To, #iq{type = Type, lang = Lang,
[From, To, Node, Lang]) of
{result, Features} ->
ANode = case Node of
[] -> [];
_ -> [{"node", SNode}]
"" -> [];
_ -> [{"node", Node}]
end,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@@ -252,7 +250,7 @@ get_vh_services(Host) ->
process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
case Type of
set ->
#jid{user = User, luser = LTo, lserver = ToServer} = To,
#jid{luser = LTo, lserver = ToServer} = To,
#jid{luser = LFrom, lserver = LServer} = From,
Self = (LTo == LFrom) andalso (ToServer == LServer),
Node = xml:get_tag_attr_s("node", SubEl),
@@ -272,16 +270,15 @@ process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ
end;
get ->
Host = To#jid.lserver,
SNode = xml:get_tag_attr_s("node", SubEl),
Node = string:tokens(SNode, "/"),
Node = xml:get_tag_attr_s("node", SubEl),
case ejabberd_hooks:run_fold(disco_sm_items,
Host,
empty,
[From, To, Node, Lang]) of
{result, Items} ->
ANode = case Node of
[] -> [];
_ -> [{"node", SNode}]
"" -> [];
_ -> [{"node", Node}]
end,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@@ -329,8 +326,7 @@ process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
Host = To#jid.lserver,
SNode = xml:get_tag_attr_s("node", SubEl),
Node = string:tokens(SNode, "/"),
Node = xml:get_tag_attr_s("node", SubEl),
Identity = ejabberd_hooks:run_fold(disco_sm_identity,
Host,
[],
@@ -341,8 +337,8 @@ process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
[From, To, Node, Lang]) of
{result, Features} ->
ANode = case Node of
[] -> [];
_ -> [{"node", SNode}]
"" -> [];
_ -> [{"node", Node}]
end,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@@ -384,24 +380,17 @@ get_user_resources(User, Server) ->
get_publish_items(empty,
#jid{luser = LFrom, lserver = LSFrom},
#jid{user = User, server = Server, luser = LTo, lserver = LSTo} = _To,
#jid{luser = LTo, lserver = LSTo} = _To,
Node, _Lang) ->
if
(LFrom == LTo) and (LSFrom == LSTo) ->
% Hack
SNode = join(Node, "/"),
retrieve_disco_publish({LTo, LSTo}, SNode);
retrieve_disco_publish({LTo, LSTo}, Node);
true ->
empty
end;
get_publish_items(Acc, _From, _To, _Node, _Lang) ->
Acc.
join(List, Sep) ->
lists:foldl(fun(A, "") -> A;
(A, Acc) -> Acc ++ Sep ++ A
end, "", List).
process_disco_publish(User, Node, Items) ->
F = fun() ->
lists:foreach(
+101 -22
View File
@@ -10,38 +10,117 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
-behaviour(gen_mod).
-export([start/2, init/1, stop/1]).
%% API
-export([start_link/2, start/2, stop/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(state, {host}).
-define(PROCNAME, ejabberd_mod_echo).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
MyHost = gen_mod:get_opt(host, Opts, "echo." ++ Host),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
spawn(?MODULE, init, [MyHost])).
init(Host) ->
ejabberd_router:register_route(Host),
loop(Host).
loop(Host) ->
receive
{route, From, To, Packet} ->
ejabberd_router:route(To, From, Packet),
loop(Host);
stop ->
ejabberd_router:unregister_route(Host),
ok;
_ ->
loop(Host)
end.
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
temporary,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
Proc ! stop,
{wait, Proc}.
gen_server:call(Proc, stop),
supervisor:stop_child(ejabberd_sup, Proc).
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Opts]) ->
MyHost = gen_mod:get_opt(host, Opts, "echo." ++ Host),
ejabberd_router:register_route(MyHost),
{ok, #state{host = MyHost}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(stop, _From, State) ->
{stop, normal, ok, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) ->
ejabberd_router:route(To, From, Packet),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, State) ->
ejabberd_router:unregister_route(State#state.host),
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+18 -10
View File
@@ -55,27 +55,35 @@ static int iconv_erl_control(ErlDrvData drv_data,
ei_get_type(buf, &index, &i, &size);
stmp = string = malloc(size + 1);
ei_decode_string(buf, &index, string);
cd = iconv_open(to, from);
if(cd == (iconv_t) -1)
{
if (cd == (iconv_t) -1) {
cd = iconv_open("ascii", "ascii");
if(cd == (iconv_t) -1)
{
cd = iconv_open("ascii", "ascii");
}
if (cd == (iconv_t) -1) {
*rbuf = (char*)(b = driver_alloc_binary(size));
memcpy(b->orig_bytes, string, size);
free(from);
free(to);
free(string);
return size;
}
}
outleft = avail = 4*size;
inleft = size;
rtmp = rstring = malloc(avail);
iconv(cd, &stmp, &inleft, &rtmp, &outleft);
while (inleft > 0) {
if (iconv(cd, &stmp, &inleft, &rtmp, &outleft) == (size_t) -1) {
stmp++;
inleft--;
}
}
size = rtmp - rstring;
//printf("size=%d, res=%s\r\n", size, rstring);
*rbuf = (char*)(b = driver_alloc_binary(size));
memcpy(b->orig_bytes, rstring, size);
+136 -33
View File
@@ -10,12 +10,20 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
-behaviour(gen_mod).
-export([start/2, init/2, stop/1,
%% API
-export([start_link/2,
start/2,
stop/1,
closed_connection/3,
get_user_and_encoding/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
@@ -24,9 +32,51 @@
-record(irc_connection, {jid_server_host, pid}).
-record(irc_custom, {us_host, data}).
-record(state, {host, server_host, access}).
-define(PROCNAME, ejabberd_mod_irc).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
start_supervisor(Host),
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
temporary,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
stop_supervisor(Host),
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:delete_child(ejabberd_sup, Proc).
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Opts]) ->
iconv:start(),
mnesia:create_table(irc_custom,
[{disc_copies, [node()]},
@@ -34,38 +84,97 @@ start(Host, Opts) ->
MyHost = gen_mod:get_opt(host, Opts, "irc." ++ Host),
update_table(MyHost),
Access = gen_mod:get_opt(access, Opts, all),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
spawn(?MODULE, init, [MyHost, Access])).
init(Host, Access) ->
catch ets:new(irc_connection, [named_table,
public,
{keypos, #irc_connection.jid_server_host}]),
ejabberd_router:register_route(Host),
loop(Host, Access).
ejabberd_router:register_route(MyHost),
{ok, #state{host = MyHost,
server_host = Host,
access = Access}}.
loop(Host, Access) ->
receive
{route, From, To, Packet} ->
case catch do_route(Host, Access, From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]);
_ ->
ok
end,
loop(Host, Access);
stop ->
ejabberd_router:unregister_route(Host),
ok;
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(stop, _From, State) ->
{stop, normal, ok, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet},
#state{host = Host,
server_host = ServerHost,
access = Access} = State) ->
case catch do_route(Host, ServerHost, Access, From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]);
_ ->
loop(Host, Access)
end.
ok
end,
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, State) ->
ejabberd_router:unregister_route(State#state.host),
ok.
do_route(Host, Access, From, To, Packet) ->
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
start_supervisor(Host) ->
Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup),
ChildSpec =
{Proc,
{ejabberd_tmp_sup, start_link,
[Proc, mod_irc_connection]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop_supervisor(Host) ->
Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
do_route(Host, ServerHost, Access, From, To, Packet) ->
case acl:match_rule(Host, Access, From) of
allow ->
do_route1(Host, From, To, Packet);
do_route1(Host, ServerHost, From, To, Packet);
_ ->
{xmlelement, _Name, Attrs, _Els} = Packet,
Lang = xml:get_attr_s("xml:lang", Attrs),
@@ -75,7 +184,7 @@ do_route(Host, Access, From, To, Packet) ->
ejabberd_router:route(To, From, Err)
end.
do_route1(Host, From, To, Packet) ->
do_route1(Host, ServerHost, From, To, Packet) ->
#jid{user = ChanServ, resource = Resource} = To,
{xmlelement, _Name, Attrs, _Els} = Packet,
case ChanServ of
@@ -132,7 +241,7 @@ do_route1(Host, From, To, Packet) ->
{Username, Encoding} = get_user_and_encoding(
Host, From, Server),
{ok, Pid} = mod_irc_connection:start(
From, Host, Server,
From, Host, ServerHost, Server,
Username, Encoding),
ets:insert(
irc_connection,
@@ -174,12 +283,6 @@ do_route1(Host, From, To, Packet) ->
end.
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
Proc ! stop,
{wait, Proc}.
closed_connection(Host, From, Server) ->
ets:delete(irc_connection, {From, Server, Host}).
@@ -204,7 +307,7 @@ iq_get_vcard(Lang) ->
"http://ejabberd.jabberstudio.org/"}]},
{xmlelement, "DESC", [],
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module\n"
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
"Copyright (c) 2003-2006 Alexey Shchepin")}]}].
process_register(Host, From, To, #iq{} = IQ) ->
case catch process_irc_register(Host, From, To, IQ) of
+184 -90
View File
@@ -13,7 +13,7 @@
-behaviour(gen_fsm).
%% External exports
-export([start/5, receiver/2, route_chan/4, route_nick/3]).
-export([start_link/5, start/6, route_chan/4, route_nick/3]).
%% gen_fsm callbacks
-export([init/1,
@@ -31,7 +31,7 @@
-define(SETS, gb_sets).
-record(state, {socket, encoding, receiver, queue,
-record(state, {socket, encoding, queue,
user, host, server, nick,
channels = dict:new(),
inbuf = "", outbuf = ""}).
@@ -47,8 +47,14 @@
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start(From, Host, Server, Username, Encoding) ->
gen_fsm:start(?MODULE, [From, Host, Server, Username, Encoding], ?FSMOPTS).
start(From, Host, ServerHost, Server, Username, Encoding) ->
Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_irc_sup),
supervisor:start_child(
Supervisor, [From, Host, Server, Username, Encoding]).
start_link(From, Host, Server, Username, Encoding) ->
gen_fsm:start_link(?MODULE, [From, Host, Server, Username, Encoding],
?FSMOPTS).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
@@ -109,7 +115,7 @@ open_socket(init, StateData) ->
wait_for_registration(closed, StateData) ->
{stop, normal, StateData}.
stream_established({xmlstreamend, Name}, StateData) ->
stream_established({xmlstreamend, _Name}, StateData) ->
{stop, normal, StateData};
stream_established(timeout, StateData) ->
@@ -139,7 +145,7 @@ stream_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}.
%%----------------------------------------------------------------------
@@ -151,11 +157,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}.
-define(SEND(S),
@@ -237,15 +243,36 @@ handle_info({route_chan, Channel, Resource,
case Body of
"/quote " ++ Rest ->
?SEND(Rest ++ "\r\n");
"/msg " ++ Rest ->
?SEND("PRIVMSG " ++ Rest ++ "\r\n");
"/me " ++ Rest ->
Strings = string:tokens(Rest, "\n"),
Res = lists:concat(
lists:map(
fun(S) ->
io_lib:format(
"PRIVMSG #~s :\001ACTION ~s\001\r\n",
[Channel, S])
end, Strings)),
?SEND(Res);
"/ctcp " ++ Rest ->
Words = string:tokens(Rest, " "),
case Words of
[CtcpDest | _] ->
CtcpCmd =
toupper(
string:substr(
Rest,
string:str(Rest, " ") + 1)),
Res = io_lib:format(
"PRIVMSG ~s :\001~s\001\r\n",
[CtcpDest, CtcpCmd]),
?SEND(Res);
_ ->
ok
end;
_ ->
Body1 =
case Body of
[$/, $m, $e, $ | Rest] ->
"\001ACTION " ++ Rest ++ "\001";
_ ->
Body
end,
Strings = string:tokens(Body1, "\n"),
Strings = string:tokens(Body, "\n"),
Res = lists:concat(
lists:map(
fun(S) ->
@@ -265,19 +292,40 @@ handle_info({route_chan, Channel, Resource,
end, Strings)),
?SEND(Res)
end;
"chat" ->
Type when Type == "chat"; Type == ""; Type == "normal" ->
Body = xml:get_path_s(El, [{elem, "body"}, cdata]),
case Body of
"/quote " ++ Rest ->
?SEND(Rest ++ "\r\n");
"/msg " ++ Rest ->
?SEND("PRIVMSG " ++ Rest ++ "\r\n");
"/me " ++ Rest ->
Strings = string:tokens(Rest, "\n"),
Res = lists:concat(
lists:map(
fun(S) ->
io_lib:format(
"PRIVMSG ~s :\001ACTION ~s\001\r\n",
[Resource, S])
end, Strings)),
?SEND(Res);
"/ctcp " ++ Rest ->
Words = string:tokens(Rest, " "),
case Words of
[CtcpDest | _ ] ->
CtcpCmd =
toupper(
string:substr(
Rest, string:str(Rest, " ") + 1)),
Res = io_lib:format(
"PRIVMSG ~s :~s\r\n",
[CtcpDest, "\001" ++ CtcpCmd ++ "\001"]),
?SEND(Res);
_ ->
ok
end;
_ ->
Body1 = case Body of
[$/, $m, $e, $ | Rest] ->
"\001ACTION " ++ Rest ++ "\001";
_ ->
Body
end,
Strings = string:tokens(Body1, "\n"),
Strings = string:tokens(Body, "\n"),
Res = lists:concat(
lists:map(
fun(S) ->
@@ -352,14 +400,32 @@ handle_info({route_nick, Nick,
case Body of
"/quote " ++ Rest ->
?SEND(Rest ++ "\r\n");
"/msg " ++ Rest ->
?SEND("PRIVMSG " ++ Rest ++ "\r\n");
"/me " ++ Rest ->
Strings = string:tokens(Rest, "\n"),
Res = lists:concat(
lists:map(
fun(S) ->
io_lib:format(
"PRIVMSG ~s :\001ACTION ~s\001\r\n",
[Nick, S])
end, Strings)),
?SEND(Res);
"/ctcp " ++ Rest ->
Words = string:tokens(Rest, " "),
case Words of
[CtcpDest | _ ] ->
CtcpCmd = toupper(string:substr(Rest, string:str(Rest, " ")+1 )),
Res = io_lib:format(
"PRIVMSG ~s :~s\r\n",
[CtcpDest, "\001" ++ CtcpCmd ++ "\001"]),
?SEND(Res);
_ ->
ok
end;
_ ->
Body1 = case Body of
[$/, $m, $e, $ | Rest] ->
"\001ACTION " ++ Rest ++ "\001";
_ ->
Body
end,
Strings = string:tokens(Body1, "\n"),
Strings = string:tokens(Body, "\n"),
Res = lists:concat(
lists:map(
fun(S) ->
@@ -397,6 +463,9 @@ handle_info({ircstring, [$: | String]}, StateName, StateData) ->
[_, "332", Nick, [$# | Chan] | _] ->
process_channel_topic(StateData, Chan, String),
StateData;
[_, "333", Nick, [$# | Chan] | _] ->
process_channel_topic_who(StateData, Chan, String),
StateData;
[_, "318", _, Nick | _] ->
process_endofwhois(StateData, String, Nick),
StateData;
@@ -445,7 +514,7 @@ handle_info({ircstring, [$: | String]}, StateName, StateData) ->
"member", "participant"),
StateData;
[From, "KICK", [$# | Chan], Nick | _] ->
process_kick(StateData, Chan, From, Nick),
process_kick(StateData, Chan, From, Nick, String),
StateData;
[From, "NICK", Nick | _] ->
process_nick(StateData, From, Nick);
@@ -523,21 +592,6 @@ terminate(Reason, StateName, StateData) ->
%%% Internal functions
%%%----------------------------------------------------------------------
receiver(Socket, C2SPid) ->
XMLStreamPid = xml_stream:start(C2SPid),
receiver(Socket, C2SPid, XMLStreamPid).
receiver(Socket, C2SPid, XMLStreamPid) ->
case gen_tcp:recv(Socket, 0) of
{ok, Text} ->
xml_stream:send_text(XMLStreamPid, Text),
receiver(Socket, C2SPid, XMLStreamPid);
{error, Reason} ->
exit(XMLStreamPid, closed),
gen_fsm:send_event(C2SPid, closed),
ok
end.
send_text(#state{socket = Socket, encoding = Encoding}, Text) ->
CText = iconv:convert("utf-8", Encoding, lists:flatten(Text)),
%?DEBUG("IRC OUTu: ~s~nIRC OUTk: ~s~n", [Text, CText]),
@@ -611,6 +665,9 @@ process_channel_list_user(StateData, Chan, User) ->
case User1 of
[$@ | U2] -> {U2, "admin", "moderator"};
[$+ | U2] -> {U2, "member", "participant"};
[$\% | U2] -> {U2, "admin", "moderator"};
[$& | U2] -> {U2, "admin", "moderator"};
[$~ | U2] -> {U2, "admin", "moderator"};
_ -> {User1, "member", "participant"}
end,
ejabberd_router:route(
@@ -635,16 +692,43 @@ process_channel_list_user(StateData, Chan, User) ->
process_channel_topic(StateData, Chan, String) ->
FromUser = "someone",
{ok, Msg, _} = regexp:sub(String, ".*332[^:]*:", ""),
Msg1 = filter_message(Msg),
ejabberd_router:route(
jlib:make_jid(
lists:concat([Chan, "%", StateData#state.server]),
StateData#state.host, FromUser),
StateData#state.host, ""),
StateData#state.user,
{xmlelement, "message", [{"type", "groupchat"}],
[{xmlelement, "subject", [], [{xmlcdata, Msg1}]}]}).
[{xmlelement, "subject", [], [{xmlcdata, Msg1}]},
{xmlelement, "body", [], [{xmlcdata, "Topic for #" ++ Chan ++ ": " ++ Msg1}]}
]}).
process_channel_topic_who(StateData, Chan, String) ->
Words = string:tokens(String, " "),
Msg1 = case Words of
[_, "333", _, _Chan, Whoset , Timeset] ->
case string:to_integer(Timeset) of
{Unixtimeset, Rest} ->
"Topic for #" ++ Chan ++ " set by " ++ Whoset ++
" at " ++ unixtime2string(Unixtimeset);
_->
"Topic for #" ++ Chan ++ " set by " ++ Whoset
end;
[_, "333", _, _Chan, Whoset | _] ->
"Topic for #" ++ Chan ++ " set by " ++ Whoset;
_ ->
String
end,
Msg2 = filter_message(Msg1),
ejabberd_router:route(
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
StateData#state.host, ""),
StateData#state.user,
{xmlelement, "message", [{"type", "groupchat"}],
[{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}).
process_endofwhois(StateData, String, Nick) ->
@@ -717,7 +801,7 @@ process_channotice(StateData, Chan, From, String) ->
[1, $A, $C, $T, $I, $O, $N, $ | Rest] ->
"/me " ++ Rest;
_ ->
Msg
"/me NOTICE: " ++ Msg
end,
Msg2 = filter_message(Msg1),
ejabberd_router:route(
@@ -725,7 +809,7 @@ process_channotice(StateData, Chan, From, String) ->
StateData#state.host, FromUser),
StateData#state.user,
{xmlelement, "message", [{"type", "groupchat"}],
[{xmlelement, "body", [], [{xmlcdata, "NOTICE: " ++ Msg2}]}]}).
[{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}).
@@ -755,7 +839,7 @@ process_notice(StateData, Nick, From, String) ->
[1, $A, $C, $T, $I, $O, $N, $ | Rest] ->
"/me " ++ Rest;
_ ->
Msg
"/me NOTICE: " ++ Msg
end,
Msg2 = filter_message(Msg1),
ejabberd_router:route(
@@ -763,7 +847,7 @@ process_notice(StateData, Nick, From, String) ->
StateData#state.host, ""),
StateData#state.user,
{xmlelement, "message", [{"type", "chat"}],
[{xmlelement, "body", [], [{xmlcdata, "NOTICE: " ++ Msg2}]}]}).
[{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}).
process_version(StateData, Nick, From) ->
@@ -785,7 +869,7 @@ process_userinfo(StateData, Nick, From) ->
send_text(
StateData,
io_lib:format("NOTICE ~s :\001USERINFO "
"This user uses xmpp:~s"
"xmpp:~s"
"\001\r\n",
[FromUser,
jlib:jid_to_string(StateData#state.user)])).
@@ -807,17 +891,8 @@ process_topic(StateData, Chan, From, String) ->
process_part(StateData, Chan, From, String) ->
[FromUser | FromIdent] = string:tokens(From, "!"),
{ok, Msg, _} = regexp:sub(String, ".*PART[^:]*", ""),
{ok, Msg, _} = regexp:sub(String, ".*PART[^:]*:", ""),
Msg1 = filter_message(Msg),
ejabberd_router:route(
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
StateData#state.host, FromUser),
StateData#state.user,
{xmlelement, "message", [{"type", "groupchat"}],
[{xmlelement, "body", [],
[{xmlcdata, "/me has part: " ++
Msg1 ++ "(" ++ FromIdent ++ ")" }]}]}),
ejabberd_router:route(
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
StateData#state.host, FromUser),
@@ -829,7 +904,7 @@ process_part(StateData, Chan, From, String) ->
{"role", "none"}],
[]}]},
{xmlelement, "status", [],
[{xmlcdata, Msg1 ++ "(" ++ FromIdent ++ ")"}]}]
[{xmlcdata, Msg1 ++ " (" ++ FromIdent ++ ")"}]}]
}),
case catch dict:update(Chan,
fun(Ps) ->
@@ -852,16 +927,6 @@ process_quit(StateData, From, String) ->
fun(Chan, Ps) ->
case ?SETS:is_member(FromUser, Ps) of
true ->
ejabberd_router:route(
jlib:make_jid(
lists:concat([Chan, "%", StateData#state.server]),
StateData#state.host, FromUser),
StateData#state.user,
{xmlelement, "message", [{"type", "groupchat"}],
[{xmlelement, "body", [],
[{xmlcdata, "/me has quit: " ++
Msg1 ++ "(" ++ FromIdent ++ ")" }]}]}),
ejabberd_router:route(
jlib:make_jid(
lists:concat([Chan, "%", StateData#state.server]),
@@ -874,7 +939,7 @@ process_quit(StateData, From, String) ->
{"role", "none"}],
[]}]},
{xmlelement, "status", [],
[{xmlcdata, Msg1 ++ "(" ++ FromIdent ++ ")"}]}
[{xmlcdata, Msg1 ++ " (" ++ FromIdent ++ ")"}]}
]}),
remove_element(FromUser, Ps);
_ ->
@@ -899,16 +964,6 @@ process_join(StateData, Channel, From, String) ->
[]}]},
{xmlelement, "status", [],
[{xmlcdata, FromIdent}]}]}),
{ok, Msg, _} = regexp:sub(String, ".*JOIN[^:]*:", ""),
Msg1 = filter_message(Msg),
ejabberd_router:route(
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
StateData#state.host, FromUser),
StateData#state.user,
{xmlelement, "message", [{"type", "groupchat"}],
[{xmlelement, "body", [],
[{xmlcdata, "/me has joined " ++
Msg1 ++ "(" ++ FromIdent ++ ")" }]}]}),
case catch dict:update(Chan,
fun(Ps) ->
@@ -935,8 +990,15 @@ process_mode_o(StateData, Chan, From, Nick, Affiliation, Role) ->
{"role", Role}],
[]}]}]}).
process_kick(StateData, Chan, From, Nick) ->
%Msg = lists:last(string:tokens(String, ":")),
process_kick(StateData, Chan, From, Nick, String) ->
Msg = lists:last(string:tokens(String, ":")),
Msg2 = Nick ++ " kicked by " ++ From ++ " (" ++ filter_message(Msg) ++ ")",
ejabberd_router:route(
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
StateData#state.host, ""),
StateData#state.user,
{xmlelement, "message", [{"type", "groupchat"}],
[{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}),
ejabberd_router:route(
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
StateData#state.host, Nick),
@@ -1095,4 +1157,36 @@ filter_message(Msg) ->
false;
true -> true
end
end, Msg).
end, filter_mirc_colors(Msg)).
filter_mirc_colors(Msg) ->
case regexp:gsub(Msg, "(\\003[0-9]+)(,[0-9]+)?", "") of
{ok, Msg2, _} ->
Msg2;
_ ->
Msg
end.
unixtime2string(Unixtime) ->
Secs = Unixtime + calendar:datetime_to_gregorian_seconds(
{{1970, 1, 1}, {0,0,0}}),
case calendar:universal_time_to_local_time(
calendar:gregorian_seconds_to_datetime(Secs)) of
{{Year, Month, Day}, {Hour, Minute, Second}} ->
lists:flatten(
io_lib:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",
[Year, Month, Day, Hour, Minute, Second]));
_->
"0000-00-00 00:00:00"
end.
toupper([C | Cs]) ->
if
C >= $a, C =< $z ->
[C - 32 | toupper(Cs)];
true ->
[C | toupper(Cs)]
end;
toupper([]) ->
[].
+8 -15
View File
@@ -56,7 +56,6 @@ process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
[]}]}
end.
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
set ->
@@ -99,7 +98,7 @@ get_last(IQ, SubEl, LUser, LServer) ->
case catch ejabberd_odbc:sql_query(
LServer,
["select seconds, state from last "
"where username='", Username, "'"]) of
"where username='", Username, "';"]) of
{'EXIT', _Reason} ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
{selected, ["seconds","state"], []} ->
@@ -121,8 +120,6 @@ get_last(IQ, SubEl, LUser, LServer) ->
end
end.
on_presence_update(User, Server, _Resource, Status) ->
{MegaSecs, Secs, _MicroSecs} = now(),
TimeStamp = MegaSecs * 1000000 + Secs,
@@ -134,20 +131,16 @@ store_last_info(User, Server, TimeStamp, Status) ->
Username = ejabberd_odbc:escape(LUser),
Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)),
State = ejabberd_odbc:escape(Status),
ejabberd_odbc:sql_query(
%% MREMOND: I think this should be turn into a non transactional behaviour
ejabberd_odbc:sql_transaction(
LServer,
["begin;"
"delete from last where username='", Username, "';"
"insert into last(username, seconds, state) "
"values ('", Username, "', '", Seconds, "', '", State, "');",
"commit"]).
[["delete from last where username='", Username, "';"],
["insert into last(username, seconds, state) "
"values ('", Username, "', '", Seconds, "', '", State, "');"]]).
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
Username = ejabberd_odbc:escape(LUser),
ejabberd_odbc:sql_query(
LServer,
["delete from last where username='", Username, "'"]).
ejabberd_odbc:sql_query(LServer,
["delete from last where username='", Username, "';"]).
+1
View File
@@ -12,6 +12,7 @@ OUTDIR = ..
EFLAGS = -I .. -pz ..
OBJS = \
$(OUTDIR)/mod_muc.beam \
$(OUTDIR)/mod_muc_log.beam \
$(OUTDIR)/mod_muc_room.beam
all: $(OBJS)
+4
View File
@@ -6,6 +6,7 @@ EFLAGS = -I .. -pz ..
OBJS = \
$(OUTDIR)\mod_muc.beam \
$(OUTDIR)\mod_muc_log.beam \
$(OUTDIR)\mod_muc_room.beam
ALL : $(OBJS)
@@ -16,5 +17,8 @@ CLEAN :
$(OUTDIR)\mod_muc.beam : mod_muc.erl
erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc.erl
$(OUTDIR)\mod_muc_log.beam : mod_muc_log.erl
erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc_log.erl
$(OUTDIR)\mod_muc_room.beam : mod_muc_room.erl
erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc_room.erl
+254 -153
View File
@@ -10,10 +10,12 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
-behaviour(gen_mod).
-export([start/2,
init/3,
%% API
-export([start_link/2,
start/2,
stop/1,
room_destroyed/3,
store_room/3,
@@ -22,6 +24,10 @@
process_iq_disco_items/4,
can_use_nick/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
@@ -30,9 +36,106 @@
-record(muc_online_room, {name_host, pid}).
-record(muc_registered, {us_host, nick}).
-record(state, {host, server_host, access}).
-define(PROCNAME, ejabberd_mod_muc).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
start_supervisor(Host),
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
temporary,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
stop_supervisor(Host),
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:delete_child(ejabberd_sup, Proc).
room_destroyed(Host, Room, ServerHost) ->
gen_mod:get_module_proc(ServerHost, ?PROCNAME) !
{room_destroyed, {Room, Host}},
ok.
store_room(Host, Name, Opts) ->
F = fun() ->
mnesia:write(#muc_room{name_host = {Name, Host},
opts = Opts})
end,
mnesia:transaction(F).
restore_room(Host, Name) ->
case catch mnesia:dirty_read(muc_room, {Name, Host}) of
[#muc_room{opts = Opts}] ->
Opts;
_ ->
error
end.
forget_room(Host, Name) ->
F = fun() ->
mnesia:delete({muc_room, {Name, Host}})
end,
mnesia:transaction(F).
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)).
can_use_nick(_Host, _JID, "") ->
false;
can_use_nick(Host, JID, Nick) ->
{LUser, LServer, _} = jlib:jid_tolower(JID),
LUS = {LUser, LServer},
case catch mnesia:dirty_select(
muc_registered,
[{#muc_registered{us_host = '$1',
nick = Nick,
_ = '_'},
[{'==', {element, 2, '$1'}, Host}],
['$_']}]) of
{'EXIT', _Reason} ->
true;
[] ->
true;
[#muc_registered{us_host = {U, _Host}}] ->
U == LUS
end.
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Opts]) ->
mnesia:create_table(muc_room,
[{disc_copies, [node()]},
{attributes, record_info(fields, muc_room)}]),
@@ -45,40 +148,96 @@ start(Host, Opts) ->
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(gen_mod:get_module_proc(Host, ?PROCNAME),
spawn(?MODULE, init,
[MyHost, Host, {Access, AccessCreate, AccessAdmin}])).
init(Host, ServerHost, Access) ->
catch ets:new(muc_online_room, [named_table,
public,
{keypos, #muc_online_room.name_host}]),
ejabberd_router:register_route(Host),
load_permanent_rooms(Host, ServerHost, Access),
loop(Host, ServerHost, Access).
ejabberd_router:register_route(MyHost),
load_permanent_rooms(MyHost, Host, {Access, AccessCreate, AccessAdmin}),
{ok, #state{host = MyHost,
server_host = Host,
access = {Access, AccessCreate, AccessAdmin}}}.
loop(Host, ServerHost, Access) ->
receive
{route, From, To, Packet} ->
case catch do_route(Host, ServerHost, Access, From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]);
_ ->
ok
end,
loop(Host, ServerHost, Access);
{room_destroyed, RoomHost} ->
ets:delete(muc_online_room, RoomHost),
loop(Host, ServerHost, Access);
stop ->
ejabberd_router:unregister_route(Host),
ok;
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(stop, _From, State) ->
{stop, normal, ok, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet},
#state{host = Host,
server_host = ServerHost,
access = Access} = State) ->
case catch do_route(Host, ServerHost, Access, From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]);
_ ->
loop(Host, ServerHost, Access)
end.
ok
end,
{noreply, State};
handle_info({room_destroyed, RoomHost}, State) ->
ets:delete(muc_online_room, RoomHost),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, State) ->
ejabberd_router:unregister_route(State#state.host),
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
start_supervisor(Host) ->
Proc = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup),
ChildSpec =
{Proc,
{ejabberd_tmp_sup, start_link,
[Proc, mod_muc_room]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop_supervisor(Host) ->
Proc = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
do_route(Host, ServerHost, Access, From, To, Packet) ->
{AccessRoute, _AccessCreate, _AccessAdmin} = Access,
@@ -107,7 +266,7 @@ do_route1(Host, ServerHost, Access, From, To, Packet) ->
"iq" ->
case jlib:iq_query_info(Packet) of
#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
sub_el = SubEl} = IQ ->
sub_el = _SubEl} = IQ ->
Res = IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS}],
@@ -123,7 +282,7 @@ do_route1(Host, ServerHost, Access, From, To, Packet) ->
#iq{type = get,
xmlns = ?NS_REGISTER = XMLNS,
lang = Lang,
sub_el = SubEl} = IQ ->
sub_el = _SubEl} = IQ ->
Res = IQ#iq{type = result,
sub_el =
[{xmlelement, "query",
@@ -155,7 +314,7 @@ do_route1(Host, ServerHost, Access, From, To, Packet) ->
#iq{type = get,
xmlns = ?NS_VCARD = XMLNS,
lang = Lang,
sub_el = SubEl} = IQ ->
sub_el = _SubEl} = IQ ->
Res = IQ#iq{type = result,
sub_el =
[{xmlelement, "vCard",
@@ -252,40 +411,6 @@ do_route1(Host, ServerHost, Access, From, To, Packet) ->
room_destroyed(Host, Room, ServerHost) ->
gen_mod:get_module_proc(ServerHost, ?PROCNAME) !
{room_destroyed, {Room, Host}},
ok.
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
Proc ! stop,
{wait, Proc}.
store_room(Host, Name, Opts) ->
F = fun() ->
mnesia:write(#muc_room{name_host = {Name, Host},
opts = Opts})
end,
mnesia:transaction(F).
restore_room(Host, Name) ->
case catch mnesia:dirty_read(muc_room, {Name, Host}) of
[#muc_room{opts = Opts}] ->
Opts;
_ ->
error
end.
forget_room(Host, Name) ->
F = fun() ->
mnesia:delete({muc_room, {Name, Host}})
end,
mnesia:transaction(F).
load_permanent_rooms(Host, ServerHost, Access) ->
case catch mnesia:dirty_select(
muc_room, [{#muc_room{name_host = {'_', Host}, _ = '_'},
@@ -321,15 +446,6 @@ iq_disco_info() ->
{xmlelement, "feature", [{"var", ?NS_VCARD}], []}].
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, Lang) ->
lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
case catch gen_fsm:sync_send_all_state_event(
@@ -380,76 +496,81 @@ iq_get_register_info(Host, From, Lang) ->
Lang, "Enter nickname you want to register")}]},
?XFIELD("text-single", "Nickname", "nick", Nick)]}].
iq_set_register_info(Host, From, XData, Lang) ->
iq_set_register_info(Host, From, Nick, Lang) ->
{LUser, LServer, _} = jlib:jid_tolower(From),
LUS = {LUser, LServer},
case lists:keysearch("nick", 1, XData) of
false ->
ErrText = "You must fill in field \"nick\" in the form",
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
{value, {_, [Nick]}} ->
F = fun() ->
case Nick of
"" ->
mnesia:delete({muc_registered, {LUS, Host}}),
F = fun() ->
case Nick of
"" ->
mnesia:delete({muc_registered, {LUS, Host}}),
ok;
_ ->
Allow =
case mnesia:select(
muc_registered,
[{#muc_registered{us_host = '$1',
nick = Nick,
_ = '_'},
[{'==', {element, 2, '$1'}, Host}],
['$_']}]) of
[] ->
true;
[#muc_registered{us_host = {U, _Host}}] ->
U == LUS
end,
if
Allow ->
mnesia:write(
#muc_registered{us_host = {LUS, Host},
nick = Nick}),
ok;
_ ->
Allow =
case mnesia:select(
muc_registered,
[{#muc_registered{us_host = '$1',
nick = Nick,
_ = '_'},
[{'==', {element, 2, '$1'}, Host}],
['$_']}]) of
[] ->
true;
[#muc_registered{us_host = {U, _Host}}] ->
U == LUS
end,
if
Allow ->
mnesia:write(
#muc_registered{us_host = {LUS, Host},
nick = Nick}),
ok;
true ->
false
end
true ->
false
end
end,
case mnesia:transaction(F) of
{atomic, ok} ->
{result, []};
{atomic, false} ->
ErrText = "Specified nickname is already registered",
{error, ?ERRT_CONFLICT(Lang, ErrText)};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end
end
end,
case mnesia:transaction(F) of
{atomic, ok} ->
{result, []};
{atomic, false} ->
ErrText = "Specified nickname is already registered",
{error, ?ERRT_CONFLICT(Lang, ErrText)};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
process_iq_register_set(Host, From, SubEl, Lang) ->
{xmlelement, _Name, _Attrs, Els} = SubEl,
case xml:remove_cdata(Els) of
[{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
case {xml:get_tag_attr_s("xmlns", XEl),
xml:get_tag_attr_s("type", XEl)} of
{?NS_XDATA, "cancel"} ->
{result, []};
{?NS_XDATA, "submit"} ->
XData = jlib:parse_xdata_submit(XEl),
case XData of
invalid ->
{error, ?ERR_BAD_REQUEST};
case xml:get_subtag(SubEl, "remove") of
false ->
case xml:remove_cdata(Els) of
[{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
case {xml:get_tag_attr_s("xmlns", XEl),
xml:get_tag_attr_s("type", XEl)} of
{?NS_XDATA, "cancel"} ->
{result, []};
{?NS_XDATA, "submit"} ->
XData = jlib:parse_xdata_submit(XEl),
case XData of
invalid ->
{error, ?ERR_BAD_REQUEST};
_ ->
case lists:keysearch("nick", 1, XData) of
false ->
ErrText = "You must fill in field \"Nickname\" in the form",
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
{value, {_, [Nick]}} ->
iq_set_register_info(Host, From, Nick, Lang)
end
end;
_ ->
iq_set_register_info(Host, From, XData, Lang)
{error, ?ERR_BAD_REQUEST}
end;
_ ->
{error, ?ERR_BAD_REQUEST}
end;
_ ->
{error, ?ERR_BAD_REQUEST}
iq_set_register_info(Host, From, "", Lang)
end.
iq_get_vcard(Lang) ->
@@ -460,7 +581,7 @@ iq_get_vcard(Lang) ->
"http://ejabberd.jabberstudio.org/"}]},
{xmlelement, "DESC", [],
[{xmlcdata, translate:translate(Lang, "ejabberd MUC module\n"
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
"Copyright (c) 2003-2006 Alexey Shchepin")}]}].
broadcast_service_message(Host, Msg) ->
@@ -477,26 +598,6 @@ get_vh_rooms(Host) ->
['$_']}]).
can_use_nick(_Host, _JID, "") ->
false;
can_use_nick(Host, JID, Nick) ->
{LUser, LServer, _} = jlib:jid_tolower(JID),
LUS = {LUser, LServer},
case catch mnesia:dirty_select(
muc_registered,
[{#muc_registered{us_host = '$1',
nick = Nick,
_ = '_'},
[{'==', {element, 2, '$1'}, Host}],
['$_']}]) of
{'EXIT', _Reason} ->
true;
[] ->
true;
[#muc_registered{us_host = {U, _Host}}] ->
U == LUS
end.
update_tables(Host) ->
update_muc_room_table(Host),
+580
View File
@@ -0,0 +1,580 @@
%%%----------------------------------------------------------------------
%%% File : mod_muc_log.erl
%%% Author : Badlop
%%% Purpose : MUC room logging
%%% Created : 12 Mar 2006 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(mod_muc_log).
-author('badlop').
-behaviour(gen_server).
-behaviour(gen_mod).
%% API
-export([start_link/2,
start/2,
stop/1,
check_access_log/2,
add_to_log/5]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-define(T(Text), translate:translate(Lang, Text)).
-define(PROCNAME, ejabberd_mod_muc_log).
-record(room, {jid, title, subject, subject_author, config}).
-record(state, {host,
out_dir,
dir_type,
css_file,
access,
lang,
timezone,
top_link}).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
temporary,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:delete_child(ejabberd_sup, Proc).
add_to_log(Host, Type, Data, Room, Opts) ->
gen_server:cast(get_proc_name(Host),
{add_to_log, Type, Data, Room, Opts}).
check_access_log(Host, From) ->
case catch gen_server:call(get_proc_name(Host),
{check_access_log, Host, From}) of
{'EXIT', _Error} ->
false;
Res ->
Res
end.
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Opts]) ->
OutDir = gen_mod:get_opt(outdir, Opts, "www/muc"),
DirType = gen_mod:get_opt(dirtype, Opts, subdirs),
CSSFile = gen_mod:get_opt(cssfile, Opts, false),
AccessLog = gen_mod:get_opt(access_log, Opts, muc_admin),
Timezone = gen_mod:get_opt(timezone, Opts, local),
Top_link = gen_mod:get_opt(top_link, Opts, {"/", "Home"}),
Lang = case ejabberd_config:get_local_option({language, Host}) of
undefined ->
"";
L ->
L
end,
{ok, #state{host = Host,
out_dir = OutDir,
dir_type = DirType,
css_file = CSSFile,
access = AccessLog,
lang = Lang,
timezone = Timezone,
top_link = Top_link}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({check_access_log, ServerHost, FromJID}, _From, State) ->
Reply = acl:match_rule(ServerHost, State#state.access, FromJID),
{reply, Reply, State};
handle_call(stop, _From, State) ->
{stop, normal, ok, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({add_to_log, Type, Data, Room, Opts}, State) ->
case catch add_to_log2(Type, Data, Room, Opts, State) of
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]);
_ ->
ok
end,
{noreply, State};
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
add_to_log2(text, {Nick, Packet}, Room, Opts, State) ->
case {xml:get_subtag(Packet, "subject"), xml:get_subtag(Packet, "body")} of
{false, false} ->
ok;
{false, SubEl} ->
Message = {body, htmlize(xml:get_tag_cdata(SubEl))},
add_message_to_log(Nick, Message, Room, Opts, State);
{SubEl, _} ->
Message = {subject, htmlize(xml:get_tag_cdata(SubEl))},
add_message_to_log(Nick, Message, Room, Opts, State)
end;
add_to_log2(roomconfig_change, _, Room, Opts, State) ->
add_message_to_log("", roomconfig_change, Room, Opts, State);
add_to_log2(nickchange, {OldNick, NewNick}, Room, Opts, State) ->
add_message_to_log(NewNick, {nickchange, OldNick}, Room, Opts, State);
add_to_log2(join, Nick, Room, Opts, State) ->
add_message_to_log(Nick, join, Room, Opts, State);
add_to_log2(leave, {Nick, Reason}, Room, Opts, State) ->
case Reason of
"" -> add_message_to_log(Nick, leave, Room, Opts, State);
_ -> add_message_to_log(Nick, {leave, Reason}, Room, Opts, State)
end;
add_to_log2(kickban, {Nick, Reason, Code}, Room, Opts, State) ->
add_message_to_log(Nick, {kickban, Code, Reason}, Room, Opts, State).
%%----------------------------------------------------------------------
%% Core
build_filename_string(TimeStamp, OutDir, RoomJID, DirType) ->
{{Year, Month, Day}, _Time} = TimeStamp,
% Directory and file names
{Dir, Filename, Rel} =
case DirType of
subdirs ->
SYear = lists:flatten(io_lib:format("~4..0w", [Year])),
SMonth = lists:flatten(io_lib:format("~2..0w", [Month])),
SDay = lists:flatten(io_lib:format("~2..0w", [Day])),
{filename:join(SYear, SMonth), SDay, "../.."};
plain ->
Date = lists:flatten(
io_lib:format("~4..0w-~2..0w-~2..0w",
[Year, Month, Day])),
{"", Date, "."}
end,
Fd = filename:join([OutDir, RoomJID, Dir]),
Fn = filename:join([Fd, Filename ++ ".html"]),
Fnrel = filename:join([Rel, Dir, Filename ++ ".html"]),
{Fd, Fn, Fnrel}.
% calculate day before
get_timestamp_daydiff(TimeStamp, Daydiff) ->
{Date1, HMS} = TimeStamp,
Date2 = calendar:gregorian_days_to_date(
calendar:date_to_gregorian_days(Date1) + Daydiff),
{Date2, HMS}.
% Try to close the previous day log, if it exists
close_previous_log(Fn) ->
case file:read_file_info(Fn) of
{ok, _} ->
{ok, F} = file:open(Fn, [append]),
fw(F, "<div class=\"legend\">ejabberd/mod_muc log<span class=\"w3c\"><a href=\"http://validator.w3.org/check?uri=referer\"><img src=\"http://www.w3.org/Icons/valid-xhtml10\" alt=\"Valid XHTML 1.0 Transitional\" height=\"31\" width=\"88\" /></a> <a href=\"http://jigsaw.w3.org/css-validator/\"><img style=\"border:0;width:88px;height:31px\" src=\"http://jigsaw.w3.org/css-validator/images/vcss\" alt=\"Valid CSS!\" /></a></span></div></body></html>"),
file:close(F);
_ -> ok
end.
add_message_to_log(Nick, Message, RoomJID, Opts, State) ->
#state{out_dir = OutDir,
dir_type = DirType,
css_file = CSSFile,
lang = Lang,
timezone = Timezone,
top_link = TopLink} = State,
Room = get_room_info(RoomJID, Opts),
TimeStamp = case Timezone of
local -> calendar:now_to_local_time(now());
universal -> calendar:now_to_universal_time(now())
end,
{Fd, Fn, _Dir} = build_filename_string(TimeStamp, OutDir, Room#room.jid, DirType),
{Date, Time} = TimeStamp,
% Open file, create if it does not exist, create parent dirs if needed
case file:read_file_info(Fn) of
{ok, _} ->
{ok, F} = file:open(Fn, [append]);
{error, enoent} ->
make_dir_rec(Fd),
{ok, F} = file:open(Fn, [append]),
Datestring = get_dateweek(Date, Lang),
TimeStampYesterday = get_timestamp_daydiff(TimeStamp, -1),
{_FdYesterday, FnYesterday, DatePrev} =
build_filename_string(
TimeStampYesterday, OutDir, Room#room.jid, DirType),
TimeStampTomorrow = get_timestamp_daydiff(TimeStamp, 1),
{_FdTomorrow, _FnTomorrow, DateNext} =
build_filename_string(
TimeStampTomorrow, OutDir, Room#room.jid, DirType),
HourOffset = calc_hour_offset(TimeStamp),
put_header(F, Room, Datestring, CSSFile, Lang,
HourOffset, DatePrev, DateNext, TopLink),
close_previous_log(FnYesterday)
end,
% Build message
Text = case Message of
roomconfig_change ->
RoomConfig = roomconfig_to_string(Room#room.config, Lang),
put_room_config(F, RoomConfig, Lang),
io_lib:format("<font class=\"mrcm\">~s</font><br/>",
[?T("Chatroom configuration modified")]);
join ->
io_lib:format("<font class=\"mj\">~s ~s</font><br/>",
[Nick, ?T("joins the room")]);
leave ->
io_lib:format("<font class=\"ml\">~s ~s</font><br/>",
[Nick, ?T("leaves the room")]);
{leave, Reason} ->
io_lib:format("<font class=\"ml\">~s ~s: ~s</font><br/>",
[Nick, ?T("leaves the room"), Reason]);
{kickban, "307", ""} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick, ?T("has been kicked")]);
{kickban, "307", Reason} ->
io_lib:format("<font class=\"mk\">~s ~s: ~s</font><br/>",
[Nick, ?T("has been kicked"), Reason]);
{kickban, "301", ""} ->
io_lib:format("<font class=\"mb\">~s ~s</font><br/>",
[Nick, ?T("has been banned")]);
{kickban, "301", Reason} ->
io_lib:format("<font class=\"mb\">~s ~s: ~s</font><br/>",
[Nick, ?T("has been banned"), Reason]);
{nickchange, OldNick} ->
io_lib:format("<font class=\"mnc\">~s ~s ~s</font><br/>",
[OldNick, ?T("is now known as"), Nick]);
{subject, T} ->
io_lib:format("<font class=\"msc\">~s~s~s</font><br/>",
[Nick, ?T(" has set the subject to: "), T]);
{body, T} ->
case regexp:first_match(T, "^/me\s") of
{match, _, _} ->
io_lib:format("<font class=\"mne\">~s ~s</font><br/>",
[Nick, string:substr(T, 5)]);
nomatch ->
io_lib:format("<font class=\"mn\">&lt;~s&gt;</font> ~s<br/>",
[Nick, T])
end
end,
{Hour, Minute, Second} = Time,
STime = lists:flatten(
io_lib:format("~2..0w:~2..0w:~2..0w", [Hour, Minute, Second])),
% Write message
file:write(F, io_lib:format("<a name=\"~s\" href=\"#~s\" class=\"ts\">[~s]</a> ~s~n",
[STime, STime, STime, Text])),
% Close file
file:close(F),
ok.
%%----------------------------------------------------------------------
%% Utilities
get_dateweek(Date, Lang) ->
Weekday = case calendar:day_of_the_week(Date) of
1 -> ?T("Monday");
2 -> ?T("Tuesday");
3 -> ?T("Wednesday");
4 -> ?T("Thursday");
5 -> ?T("Friday");
6 -> ?T("Saturday");
7 -> ?T("Sunday")
end,
{Y, M, D} = Date,
Month = case M of
1 -> ?T("January");
2 -> ?T("February");
3 -> ?T("March");
4 -> ?T("April");
5 -> ?T("May");
6 -> ?T("June");
7 -> ?T("July");
8 -> ?T("August");
9 -> ?T("September");
10 -> ?T("October");
11 -> ?T("November");
12 -> ?T("December")
end,
case Lang of
"en" -> io_lib:format("~s, ~s ~w, ~w", [Weekday, Month, D, Y]);
"es" -> io_lib:format("~s ~w de ~s de ~w", [Weekday, D, Month, Y]);
_ -> io_lib:format("~s, ~w ~s ~w", [Weekday, D, Month, Y])
end.
make_dir_rec(Dir) ->
case file:read_file_info(Dir) of
{ok, _} ->
ok;
{error, enoent} ->
DirS = filename:split(Dir),
DirR = lists:sublist(DirS, length(DirS)-1),
make_dir_rec(filename:join(DirR)),
file:make_dir(Dir)
end.
fw(F, S, O) -> io:format(F, S ++ "~n", O).
fw(F, S) -> fw(F, S, []).
put_header(F, Room, Date, CSSFile, Lang, Hour_offset, Date_prev, Date_next, Top_link) ->
fw(F, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"),
fw(F, "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"~s\" lang=\"~s\">", [Lang, Lang]),
fw(F, "<head>"),
fw(F, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"),
fw(F, "<title>~s - ~s</title>", [Room#room.title, Date]),
put_header_css(F, CSSFile),
put_header_script(F),
fw(F, "</head>"),
fw(F, "<body>"),
{Top_url, Top_text} = Top_link,
fw(F, "<div style=\"text-align: right;\"><a style=\"color: #AAAAAA; font-family: monospace; text-decoration: none; font-weight: bold;\" href=\"~s\">~s</a></div>", [Top_url, Top_text]),
fw(F, "<div class=\"roomtitle\"><a class=\"roomtitle\" href=\"xmpp:~s?join\">~s</a></div>", [Room#room.jid, Room#room.title]),
fw(F, "<a class=\"roomjid\" href=\"xmpp:~s?join\">~s</a>", [Room#room.jid, Room#room.jid]),
fw(F, "<div class=\"logdate\">~s<span class=\"w3c\"><a class=\"nav\" href=\"~s\">&lt;</a> <a class=\"nav\" href=\".\/\">^</a> <a class=\"nav\" href=\"~s\">&gt;</a></span></div>", [Date, Date_prev, Date_next]),
case {Room#room.subject_author, Room#room.subject} of
{"", ""} -> ok;
{SuA, Su} -> fw(F, "<div class=\"roomsubject\">~s~s~s</div>", [SuA, ?T(" has set the subject to: "), htmlize(Su)])
end,
RoomConfig = roomconfig_to_string(Room#room.config, Lang),
put_room_config(F, RoomConfig, Lang),
Time_offset_str = case Hour_offset<0 of
true -> io_lib:format("~p", [Hour_offset]);
false -> io_lib:format("+~p", [Hour_offset])
end,
fw(F, "<br/><a class=\"ts\">GMT~s</a><br/>", [Time_offset_str]).
put_header_css(F, false) ->
fw(F, "<style type=\"text/css\">"),
fw(F, "<!--"),
fw(F, ".ts {color: #AAAAAA; text-decoration: none;}"),
fw(F, ".mrcm {color: #009900; font-style: italic; font-weight: bold;}"),
fw(F, ".msc {color: #009900; font-style: italic; font-weight: bold;}"),
fw(F, ".mj {color: #009900; font-style: italic;}"),
fw(F, ".ml {color: #009900; font-style: italic;}"),
fw(F, ".mk {color: #009900; font-style: italic;}"),
fw(F, ".mb {color: #009900; font-style: italic;}"),
fw(F, ".mnc {color: #009900; font-style: italic;}"),
fw(F, ".mn {color: #0000AA;}"),
fw(F, ".mne {color: #AA0099;}"),
fw(F, "a.nav {color: #AAAAAA; font-family: monospace; letter-spacing: 3px; text-decoration: none;}"),
fw(F, "div.roomtitle {border-bottom: #224466 solid 3pt; margin-left: 20pt;}"),
fw(F, "a.roomtitle {color: #336699; font-size: 24px; font-weight: bold; font-family: sans-serif; letter-spacing: 3px; text-decoration: none;}"),
fw(F, "a.roomjid {color: #336699; font-size: 24px; font-weight: bold; font-family: sans-serif; letter-spacing: 3px; margin-left: 20pt; text-decoration: none;}"),
fw(F, "div.logdate {color: #663399; font-size: 20px; font-weight: bold; font-family: sans-serif; letter-spacing: 2px; border-bottom: #224466 solid 1pt; margin-left:80pt; margin-top:20px;}"),
fw(F, "div.roomsubject {color: #336699; font-size: 18px; font-family: sans-serif; margin-left: 80pt; margin-bottom: 10px;}"),
fw(F, "div.rc {color: #336699; font-size: 12px; font-family: sans-serif; margin-left: 50%; text-align: right; background: #f3f6f9; border-bottom: 1px solid #336699; border-right: 4px solid #336699;}"),
fw(F, "div.rct {font-weight: bold; background: #e3e6e9; padding-right: 10px;}"),
fw(F, "div.rcos {padding-right: 10px;}"),
fw(F, "div.rcoe {color: green;}"),
fw(F, "div.rcod {color: red;}"),
fw(F, "div.rcoe:after {content: \": v\";}"),
fw(F, "div.rcod:after {content: \": x\";}"),
fw(F, "div.rcot:after {}"),
fw(F, ".legend {width: 100%; margin-top: 30px; border-top: #224466 solid 1pt; padding: 10px 0px 10px 0px; text-align: left; font-family: monospace; letter-spacing: 2px;}"),
fw(F, ".w3c {position: absolute; right: 10px; width: 60%; text-align: right; font-family: monospace; letter-spacing: 1px;}"),
fw(F, "//-->"),
fw(F, "</style>");
put_header_css(F, CSSFile) ->
fw(F, "<link rel=\"stylesheet\" type=\"text/css\" href=\"~s\" media=\"all\">", [CSSFile]).
put_header_script(F) ->
fw(F, "<script type=\"text/javascript\">"),
fw(F, "function sh(e) // Show/Hide an element"),
fw(F, "{if(document.getElementById(e).style.display=='none')"),
fw(F, "{document.getElementById(e).style.display='block';}"),
fw(F, "else {document.getElementById(e).style.display='none';}}"),
fw(F, "</script>").
put_room_config(F, RoomConfig, Lang) ->
{_, Now2, _} = now(),
fw(F, "<div class=\"rc\">"),
fw(F, "<div class=\"rct\" onclick=\"sh('a~p');return false;\">~s</div>", [Now2, ?T("Room Configuration")]),
fw(F, "<div class=\"rcos\" id=\"a~p\" style=\"display: none;\" ><br/>~s</div>", [Now2, RoomConfig]),
fw(F, "</div>").
htmlize(S1) ->
S2_list = string:tokens(S1, "\n"),
lists:foldl(
fun(Si, Res) ->
Si2 = htmlize2(Si),
case Res of
"" -> Si2;
_ -> Res ++ "<br/>" ++ Si2
end
end,
"",
S2_list).
htmlize2(S1) ->
S2 = element(2, regexp:gsub(S1, "<", "\\&lt;")),
S3 = element(2, regexp:gsub(S2, ">", "\\&gt;")),
S4 = element(2, regexp:gsub(S3, "(http|ftp)://.[^ ]*", "<a href=\"&\">&</a>")),
%element(2, regexp:gsub(S4, " ", "\\&nbsp;")).
S4.
get_room_info(RoomJID, Opts) ->
Title =
case lists:keysearch(title, 1, Opts) of
{value, {_, T}} -> T;
false -> ""
end,
Subject =
case lists:keysearch(subject, 1, Opts) of
{value, {_, S}} -> S;
false -> ""
end,
SubjectAuthor =
case lists:keysearch(subject_author, 1, Opts) of
{value, {_, SA}} -> SA;
false -> ""
end,
#room{jid = jlib:jid_to_string(RoomJID),
title = Title,
subject = Subject,
subject_author = SubjectAuthor,
config = Opts
}.
roomconfig_to_string(Options, Lang) ->
% Get title, if available
Title = case lists:keysearch(title, 1, Options) of
{value, Tuple} -> [Tuple];
false -> []
end,
% Remove title from list
Os1 = lists:keydelete(title, 1, Options),
% Order list
Os2 = lists:sort(Os1),
% Add title to ordered list
Options2 = Title ++ Os2,
lists:foldl(
fun({Opt, Val}, R) ->
case get_roomconfig_text(Opt) of
undefined ->
R;
OptT ->
OptText = ?T(OptT),
R2 = case Val of
false -> "<div class=\"rcod\">" ++ OptText ++ "</div>";
true -> "<div class=\"rcoe\">" ++ OptText ++ "</div>";
"" -> "<div class=\"rcod\">" ++ OptText ++ "</div>";
T ->
case Opt of
password -> "<div class=\"rcoe\">" ++ OptText ++ "</div>";
title -> "<div class=\"rcot\">" ++ ?T("Room title") ++ ": \"" ++ T ++ "\"</div>";
_ -> "\"" ++ T ++ "\""
end
end,
R ++ R2
end
end,
"",
Options2).
get_roomconfig_text(title) -> "Room title";
get_roomconfig_text(persistent) -> "Make room persistent";
get_roomconfig_text(public) -> "Make room public searchable";
get_roomconfig_text(public_list) -> "Make participants list public";
get_roomconfig_text(password_protected) -> "Make room password protected";
get_roomconfig_text(password) -> "Password";
get_roomconfig_text(anonymous) -> "Make room semianonymous";
get_roomconfig_text(members_only) -> "Make room members-only";
get_roomconfig_text(moderated) -> "Make room moderated";
get_roomconfig_text(members_by_default) -> "Default users as participants";
get_roomconfig_text(allow_change_subj) -> "Allow users to change subject";
get_roomconfig_text(allow_private_messages) -> "Allow users to send private messages";
get_roomconfig_text(allow_query_users) -> "Allow users to query other users";
get_roomconfig_text(allow_user_invites) -> "Allow users to send invites";
get_roomconfig_text(logging) -> "Enable logging";
get_roomconfig_text(_) -> undefined.
get_proc_name(Host) -> gen_mod:get_module_proc(Host, ?PROCNAME).
calc_hour_offset(TimeHere) ->
TimeZero = calendar:now_to_universal_time(now()),
TimeHereHour = calendar:datetime_to_gregorian_seconds(TimeHere) div 3600,
TimeZeroHour = calendar:datetime_to_gregorian_seconds(TimeZero) div 3600,
TimeHereHour - TimeZeroHour.
+318 -160
View File
@@ -14,7 +14,9 @@
%% External exports
-export([start/6,
-export([start_link/6,
start_link/5,
start/6,
start/5,
route/4]).
@@ -49,7 +51,7 @@
password_protected = false,
password = "",
anonymous = true,
logging = false % TODO
logging = false
}).
-record(user, {jid,
@@ -84,11 +86,22 @@
%%% API
%%%----------------------------------------------------------------------
start(Host, ServerHost, Access, Room, Creator, Nick) ->
gen_fsm:start(?MODULE, [Host, ServerHost, Access, Room, Creator, Nick],
?FSMOPTS).
Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
supervisor:start_child(
Supervisor, [Host, ServerHost, Access, Room, Creator, Nick]).
start(Host, ServerHost, Access, Room, Opts) ->
gen_fsm:start(?MODULE, [Host, ServerHost, Access, Room, Opts], ?FSMOPTS).
Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
supervisor:start_child(
Supervisor, [Host, ServerHost, Access, Room, Opts]).
start_link(Host, ServerHost, Access, Room, Creator, Nick) ->
gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, Creator, Nick],
?FSMOPTS).
start_link(Host, ServerHost, Access, Room, Opts) ->
gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, Opts],
?FSMOPTS).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
@@ -101,7 +114,7 @@ start(Host, ServerHost, Access, Room, Opts) ->
%% ignore |
%% {stop, StopReason}
%%----------------------------------------------------------------------
init([Host, ServerHost, Access, Room, Creator, Nick]) ->
init([Host, ServerHost, Access, Room, Creator, _Nick]) ->
State = set_affiliation(Creator, owner,
#state{host = Host,
server_host = ServerHost,
@@ -357,7 +370,11 @@ normal_state({route, From, Nick,
NewState =
add_user_presence_un(From, Packet, StateData),
send_new_presence(From, NewState),
remove_online_user(From, NewState);
Reason = case xml:get_subtag(Packet, "status") of
false -> "";
Status_el -> xml:get_tag_cdata(Status_el)
end,
remove_online_user(From, NewState, Reason);
_ ->
StateData
end;
@@ -559,16 +576,11 @@ normal_state({route, From, ToNick,
end,
{next_state, normal_state, StateData};
normal_state(Event, StateData) ->
normal_state(_Event, StateData) ->
{next_state, normal_state, StateData}.
%%----------------------------------------------------------------------
%% Func: handle_event/3
%% Returns: {next_state, NextStateName, NextStateData} |
@@ -848,10 +860,17 @@ add_online_user(JID, Nick, Role, StateData) ->
nick = Nick,
role = Role},
StateData#state.users),
add_to_log(join, Nick, StateData),
StateData#state{users = Users}.
remove_online_user(JID, StateData) ->
remove_online_user(JID, StateData, "").
remove_online_user(JID, StateData, Reason) ->
LJID = jlib:jid_tolower(JID),
{ok, #user{nick = Nick}} =
?DICT:find(LJID, StateData#state.users),
add_to_log(leave, {Nick, Reason}, StateData),
Users = ?DICT:erase(LJID, StateData#state.users),
StateData#state{users = Users}.
@@ -862,15 +881,13 @@ filter_presence({xmlelement, "presence", Attrs, Els}) ->
case El of
{xmlcdata, _} ->
false;
{xmlelement, Name1, Attrs1, _Els1} ->
{xmlelement, _Name1, Attrs1, _Els1} ->
XMLNS = xml:get_attr_s("xmlns", Attrs1),
case {Name1, XMLNS} of
{"show", ""} ->
true;
{"status", ""} ->
true;
case XMLNS of
?NS_MUC ++ _ ->
false;
_ ->
false
true
end
end
end, Els),
@@ -971,6 +988,20 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
add_user_presence(
From, Packet,
add_online_user(From, Nick, Role, StateData)),
if not (NewState#state.config)#config.anonymous ->
WPacket = {xmlelement, "message", [{"type", "groupchat"}],
[{xmlelement, "body", [],
[{xmlcdata, translate:translate(
Lang,
"This room is not anonymous")}]},
{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
[{xmlelement, "status", [{"code", "100"}], []}]}]},
ejabberd_router:route(
StateData#state.jid,
From, WPacket);
true ->
ok
end,
send_new_presence(From, NewState),
send_existing_presences(From, NewState),
Shift = count_stanza_shift(Nick, Els, NewState),
@@ -1278,6 +1309,7 @@ change_nick(JID, Nick, StateData) ->
end, StateData#state.users),
NewStateData = StateData#state{users = Users},
send_nick_changing(JID, OldNick, NewStateData),
add_to_log(nickchange, {OldNick, Nick}, StateData),
NewStateData.
send_nick_changing(JID, OldNick, StateData) ->
@@ -1377,6 +1409,7 @@ add_message_to_history(FromNick, Packet, StateData) ->
Size = lists:flatlength(xml:element_to_string(SPacket)),
Q1 = lqueue_in({FromNick, TSPacket, HaveSubject, TimeStamp, Size},
StateData#state.history),
add_to_log(text, {FromNick, Packet}, StateData),
StateData#state{history = Q1}.
send_history(JID, Shift, StateData) ->
@@ -1544,29 +1577,41 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
catch send_kickban_presence(
JID, Reason, "307", SD),
set_role(JID, none, SD);
{JID, affiliation, none, Reason} ->
case (SD#state.config)#config.members_only of
true ->
catch send_kickban_presence(
JID, Reason, "321", SD),
SD1 = set_affiliation(JID, none, SD),
set_role(JID, none, SD1);
_ ->
SD1 = set_affiliation(JID, none, SD),
send_update_presence(JID, SD1),
SD1
end;
{JID, affiliation, outcast, Reason} ->
catch send_kickban_presence(
JID, Reason, "301", SD),
set_affiliation_and_reason(
JID, outcast, Reason,
set_role(JID, none, SD));
{JID, affiliation, A, Reason} when
{JID, affiliation, A, _Reason} when
(A == admin) or (A == owner) ->
SD1 = set_affiliation(JID, A, SD),
SD2 = set_role(JID, moderator, SD1),
send_update_presence(JID, SD2),
SD2;
{JID, affiliation, member, Reason} ->
{JID, affiliation, member, _Reason} ->
SD1 = set_affiliation(
JID, member, SD),
SD2 = set_role(JID, participant, SD1),
send_update_presence(JID, SD2),
SD2;
{JID, role, R, Reason} ->
{JID, role, R, _Reason} ->
SD1 = set_role(JID, R, SD),
catch send_new_presence(JID, SD1),
SD1;
{JID, affiliation, A, Reason} ->
{JID, affiliation, A, _Reason} ->
SD1 = set_affiliation(JID, A, SD),
send_update_presence(JID, SD1),
SD1
@@ -1593,7 +1638,7 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
end.
find_changed_items(UJID, UAffiliation, URole, [], _Lang, StateData, Res) ->
find_changed_items(_UJID, _UAffiliation, _URole, [], _Lang, _StateData, Res) ->
{result, Res};
find_changed_items(UJID, UAffiliation, URole, [{xmlcdata, _} | Items],
Lang, StateData, Res) ->
@@ -1652,10 +1697,28 @@ find_changed_items(UJID, UAffiliation, URole,
[StrAffiliation]),
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText1)};
SAffiliation ->
case can_change_ra(
UAffiliation, URole,
TAffiliation, TRole,
affiliation, SAffiliation) of
CanChangeRA =
case can_change_ra(
UAffiliation, URole,
TAffiliation, TRole,
affiliation, SAffiliation) of
nothing ->
nothing;
true ->
true;
check_owner ->
case search_affiliation(
owner, StateData) of
[{OJID, _}] ->
jlib:jid_remove_resource(OJID) /=
jlib:jid_tolower(jlib:jid_remove_resource(UJID));
_ ->
true
end;
_ ->
false
end,
case CanChangeRA of
nothing ->
find_changed_items(
UJID,
@@ -1673,7 +1736,7 @@ find_changed_items(UJID, UAffiliation, URole,
xml:get_path_s(
Item, [{elem, "reason"},
cdata])} | Res]);
_ ->
false ->
{error, ?ERR_NOT_ALLOWED}
end
end
@@ -1689,10 +1752,28 @@ find_changed_items(UJID, UAffiliation, URole,
[StrRole]),
{error, ?ERRT_BAD_REQUEST(Lang, ErrText1)};
SRole ->
case can_change_ra(
UAffiliation, URole,
TAffiliation, TRole,
role, SRole) of
CanChangeRA =
case can_change_ra(
UAffiliation, URole,
TAffiliation, TRole,
role, SRole) of
nothing ->
nothing;
true ->
true;
check_owner ->
case search_affiliation(
owner, StateData) of
[{OJID, _}] ->
jlib:jid_remove_resource(OJID) /=
jlib:jid_tolower(jlib:jid_remove_resource(UJID));
_ ->
true
end;
_ ->
false
end,
case CanChangeRA of
nothing ->
find_changed_items(
UJID,
@@ -1721,143 +1802,143 @@ find_changed_items(_UJID, _UAffiliation, _URole, _Items,
{error, ?ERR_BAD_REQUEST}.
can_change_ra(FAffiliation, FRole,
TAffiliation, TRole,
can_change_ra(_FAffiliation, _FRole,
TAffiliation, _TRole,
affiliation, Value)
when (TAffiliation == Value) ->
nothing;
can_change_ra(FAffiliation, FRole,
TAffiliation, TRole,
can_change_ra(_FAffiliation, _FRole,
_TAffiliation, TRole,
role, Value)
when (TRole == Value) ->
nothing;
can_change_ra(FAffiliation, FRole,
outcast, TRole,
can_change_ra(FAffiliation, _FRole,
outcast, _TRole,
affiliation, none)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(FAffiliation, FRole,
outcast, TRole,
can_change_ra(FAffiliation, _FRole,
outcast, _TRole,
affiliation, member)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(owner, FRole,
outcast, TRole,
can_change_ra(owner, _FRole,
outcast, _TRole,
affiliation, admin) ->
true;
can_change_ra(owner, FRole,
outcast, TRole,
can_change_ra(owner, _FRole,
outcast, _TRole,
affiliation, owner) ->
true;
can_change_ra(FAffiliation, FRole,
none, TRole,
can_change_ra(FAffiliation, _FRole,
none, _TRole,
affiliation, outcast)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(FAffiliation, FRole,
none, TRole,
can_change_ra(FAffiliation, _FRole,
none, _TRole,
affiliation, member)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(owner, FRole,
none, TRole,
can_change_ra(owner, _FRole,
none, _TRole,
affiliation, admin) ->
true;
can_change_ra(owner, FRole,
none, TRole,
can_change_ra(owner, _FRole,
none, _TRole,
affiliation, owner) ->
true;
can_change_ra(FAffiliation, FRole,
member, TRole,
can_change_ra(FAffiliation, _FRole,
member, _TRole,
affiliation, outcast)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(FAffiliation, FRole,
member, TRole,
can_change_ra(FAffiliation, _FRole,
member, _TRole,
affiliation, none)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(owner, FRole,
member, TRole,
can_change_ra(owner, _FRole,
member, _TRole,
affiliation, admin) ->
true;
can_change_ra(owner, FRole,
member, TRole,
can_change_ra(owner, _FRole,
member, _TRole,
affiliation, owner) ->
true;
can_change_ra(owner, FRole,
admin, TRole,
can_change_ra(owner, _FRole,
admin, _TRole,
affiliation, _Affiliation) ->
true;
can_change_ra(owner, FRole,
owner, TRole,
can_change_ra(owner, _FRole,
owner, _TRole,
affiliation, _Affiliation) ->
true;
can_change_ra(FAffiliation, FRole,
TAffiliation, TRole,
affiliation, Value) ->
check_owner;
can_change_ra(_FAffiliation, _FRole,
_TAffiliation, _TRole,
affiliation, _Value) ->
false;
can_change_ra(FAffiliation, moderator,
TAffiliation, visitor,
can_change_ra(_FAffiliation, moderator,
_TAffiliation, visitor,
role, none) ->
true;
can_change_ra(FAffiliation, moderator,
TAffiliation, visitor,
can_change_ra(_FAffiliation, moderator,
_TAffiliation, visitor,
role, participant) ->
true;
can_change_ra(FAffiliation, FRole,
TAffiliation, visitor,
can_change_ra(FAffiliation, _FRole,
_TAffiliation, visitor,
role, moderator)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(FAffiliation, moderator,
TAffiliation, participant,
can_change_ra(_FAffiliation, moderator,
_TAffiliation, participant,
role, none) ->
true;
can_change_ra(FAffiliation, moderator,
TAffiliation, participant,
can_change_ra(_FAffiliation, moderator,
_TAffiliation, participant,
role, visitor) ->
true;
can_change_ra(FAffiliation, FRole,
TAffiliation, participant,
can_change_ra(FAffiliation, _FRole,
_TAffiliation, participant,
role, moderator)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(FAffiliation, FRole,
can_change_ra(_FAffiliation, _FRole,
owner, moderator,
role, visitor) ->
false;
can_change_ra(owner, FRole,
TAffiliation, moderator,
can_change_ra(owner, _FRole,
_TAffiliation, moderator,
role, visitor) ->
true;
can_change_ra(FAffiliation, FRole,
can_change_ra(_FAffiliation, _FRole,
admin, moderator,
role, visitor) ->
false;
can_change_ra(admin, FRole,
TAffiliation, moderator,
can_change_ra(admin, _FRole,
_TAffiliation, moderator,
role, visitor) ->
true;
can_change_ra(FAffiliation, FRole,
can_change_ra(_FAffiliation, _FRole,
owner, moderator,
role, participant) ->
false;
can_change_ra(owner, FRole,
TAffiliation, moderator,
can_change_ra(owner, _FRole,
_TAffiliation, moderator,
role, participant) ->
true;
can_change_ra(FAffiliation, FRole,
can_change_ra(_FAffiliation, _FRole,
admin, moderator,
role, participant) ->
false;
can_change_ra(admin, FRole,
TAffiliation, moderator,
can_change_ra(admin, _FRole,
_TAffiliation, moderator,
role, participant) ->
true;
can_change_ra(FAffiliation, FRole,
TAffiliation, TRole,
role, Value) ->
can_change_ra(_FAffiliation, _FRole,
_TAffiliation, _TRole,
role, _Value) ->
false.
@@ -1883,17 +1964,20 @@ send_kickban_presence(JID, Reason, Code, StateData) ->
end
end,
lists:foreach(fun(J) ->
{ok, #user{nick = Nick}} =
?DICT:find(J, StateData#state.users),
add_to_log(kickban, {Nick, Reason, Code}, StateData),
send_kickban_presence1(J, Reason, Code, StateData)
end, LJIDs).
send_kickban_presence1(UJID, Reason, Code, StateData) ->
{ok, #user{jid = RealJID,
{ok, #user{jid = _RealJID,
nick = Nick}} =
?DICT:find(jlib:jid_tolower(UJID), StateData#state.users),
Affiliation = get_affiliation(UJID, StateData),
SAffiliation = affiliation_to_list(Affiliation),
lists:foreach(
fun({LJID, Info}) ->
fun({_LJID, Info}) ->
ItemAttrs = [{"affiliation", SAffiliation},
{"role", "none"}],
ItemEls = case Reason of
@@ -1922,20 +2006,23 @@ process_iq_owner(From, set, Lang, SubEl, StateData) ->
FAffiliation = get_affiliation(From, StateData),
case FAffiliation of
owner ->
{xmlelement, Name, Attrs, Els} = SubEl,
{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"} ->
{result, [], StateData};
{?NS_XDATA, "submit"} ->
set_config(XEl, StateData);
case check_allowed_log_change(XEl, StateData, From) of
allow -> set_config(XEl, StateData);
deny -> {error, ?ERR_BAD_REQUEST}
end;
_ ->
{error, ?ERR_BAD_REQUEST}
end;
[{xmlelement, "destroy", Attrs1, Els1}] ->
destroy_room(Els1, StateData);
[{xmlelement, "destroy", _Attrs1, _Els1} = SubEl1] ->
destroy_room(SubEl1, StateData);
Items ->
process_admin_items_set(From, Items, Lang, StateData)
end;
@@ -1948,10 +2035,10 @@ process_iq_owner(From, get, Lang, SubEl, StateData) ->
FAffiliation = get_affiliation(From, StateData),
case FAffiliation of
owner ->
{xmlelement, Name, Attrs, Els} = SubEl,
{xmlelement, _Name, _Attrs, Els} = SubEl,
case xml:remove_cdata(Els) of
[] ->
get_config(Lang, StateData);
get_config(Lang, StateData, From);
[Item] ->
case xml:get_tag_attr("affiliation", Item) of
false ->
@@ -1980,6 +2067,14 @@ process_iq_owner(From, get, Lang, SubEl, StateData) ->
{error, ?ERRT_FORBIDDEN(Lang, ErrText)}
end.
check_allowed_log_change(XEl, StateData, From) ->
case lists:keymember("logging", 1, jlib:parse_xdata_submit(XEl)) of
false ->
allow;
true ->
mod_muc_log:check_access_log(
StateData#state.server_host, From)
end.
-define(XFIELD(Type, Label, Var, Val),
@@ -2002,46 +2097,25 @@ process_iq_owner(From, get, Lang, SubEl, StateData) ->
?XFIELD("text-private", Label, Var, Val)).
get_config(Lang, StateData) ->
get_config(Lang, StateData, From) ->
Config = StateData#state.config,
Res =
[{xmlelement, "title", [],
[{xmlcdata, translate:translate(Lang, "Configuration for ") ++
jlib:jid_to_string(StateData#state.jid)}]},
?STRINGXFIELD("Room title",
"title",
Config#config.title),
?BOOLXFIELD("Allow users to change subject?",
"allow_change_subj",
Config#config.allow_change_subj),
?BOOLXFIELD("Allow users to query other users?",
"allow_query_users",
Config#config.allow_query_users),
?BOOLXFIELD("Allow users to send private messages?",
"allow_private_messages",
Config#config.allow_private_messages),
?BOOLXFIELD("Make room public searchable?",
"public",
Config#config.public),
?BOOLXFIELD("Make participants list public?",
"public_list",
Config#config.public_list),
?BOOLXFIELD("Make room persistent?",
"title",
Config#config.title),
?BOOLXFIELD("Make room persistent",
"persistent",
Config#config.persistent),
?BOOLXFIELD("Make room moderated?",
"moderated",
Config#config.moderated),
?BOOLXFIELD("Default users as members?",
"members_by_default",
Config#config.members_by_default),
?BOOLXFIELD("Make room members only?",
"members_only",
Config#config.members_only),
?BOOLXFIELD("Allow users to send invites?",
"allow_user_invites",
Config#config.allow_user_invites),
?BOOLXFIELD("Make room password protected?",
?BOOLXFIELD("Make room public searchable",
"public",
Config#config.public),
?BOOLXFIELD("Make participants list public",
"public_list",
Config#config.public_list),
?BOOLXFIELD("Make room password protected",
"password_protected",
Config#config.password_protected),
?PRIVATEXFIELD("Password",
@@ -2050,13 +2124,40 @@ get_config(Lang, StateData) ->
true -> Config#config.password;
false -> ""
end),
?BOOLXFIELD("Make room anonymous?",
?BOOLXFIELD("Make room semianonymous",
"anonymous",
Config#config.anonymous),
?BOOLXFIELD("Enable logging?",
"logging",
Config#config.logging)
],
?BOOLXFIELD("Make room members-only",
"members_only",
Config#config.members_only),
?BOOLXFIELD("Make room moderated",
"moderated",
Config#config.moderated),
?BOOLXFIELD("Default users as participants",
"members_by_default",
Config#config.members_by_default),
?BOOLXFIELD("Allow users to change subject",
"allow_change_subj",
Config#config.allow_change_subj),
?BOOLXFIELD("Allow users to send private messages",
"allow_private_messages",
Config#config.allow_private_messages),
?BOOLXFIELD("Allow users to query other users",
"allow_query_users",
Config#config.allow_query_users),
?BOOLXFIELD("Allow users to send invites",
"allow_user_invites",
Config#config.allow_user_invites)
] ++
case mod_muc_log:check_access_log(
StateData#state.server_host, From) of
allow ->
[?BOOLXFIELD(
"Enable logging",
"logging",
Config#config.logging)];
_ -> []
end,
{result, [{xmlelement, "instructions", [],
[{xmlcdata,
translate:translate(
@@ -2076,7 +2177,10 @@ set_config(XEl, StateData) ->
_ ->
case set_xoption(XData, StateData#state.config) of
#config{} = Config ->
change_config(Config, StateData);
Res = change_config(Config, StateData),
{result, _, NSD} = Res,
add_to_log(roomconfig_change, [], NSD),
Res;
Err ->
Err
end
@@ -2085,7 +2189,9 @@ set_config(XEl, StateData) ->
-define(SET_BOOL_XOPT(Opt, Val),
case Val of
"0" -> set_xoption(Opts, Config#config{Opt = false});
"false" -> set_xoption(Opts, Config#config{Opt = false});
"1" -> set_xoption(Opts, Config#config{Opt = true});
"true" -> set_xoption(Opts, Config#config{Opt = true});
_ -> {error, ?ERR_BAD_REQUEST}
end).
@@ -2125,7 +2231,7 @@ set_xoption([{"anonymous", [Val]} | Opts], Config) ->
?SET_BOOL_XOPT(anonymous, Val);
set_xoption([{"logging", [Val]} | Opts], Config) ->
?SET_BOOL_XOPT(logging, Val);
set_xoption([_ | Opts], Config) ->
set_xoption([_ | _Opts], _Config) ->
{error, ?ERR_BAD_REQUEST}.
@@ -2139,8 +2245,29 @@ change_config(Config, StateData) ->
mod_muc:forget_room(NSD#state.host, NSD#state.room);
{false, false} ->
ok
end,
{result, [], NSD}.
end,
case {(StateData#state.config)#config.members_only,
Config#config.members_only} of
{false, true} ->
NSD1 = remove_nonmembers(NSD),
{result, [], NSD1};
_ ->
{result, [], NSD}
end.
remove_nonmembers(StateData) ->
lists:foldl(
fun({_LJID, #user{jid = JID}}, SD) ->
Affiliation = get_affiliation(JID, SD),
case Affiliation of
none ->
catch send_kickban_presence(
JID, "", "322", SD),
set_role(JID, none, SD);
_ ->
SD
end
end, StateData, ?DICT:to_list(StateData#state.users)).
-define(CASE_CONFIG_OPT(Opt),
@@ -2203,17 +2330,15 @@ make_opts(StateData) ->
destroy_room(DEls, StateData) ->
destroy_room(DEl, StateData) ->
lists:foreach(
fun({LJID, Info}) ->
fun({_LJID, Info}) ->
Nick = Info#user.nick,
ItemAttrs = [{"affiliation", "none"},
{"role", "none"}],
Packet = {xmlelement, "presence", [{"type", "unavailable"}],
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
[{xmlelement, "item", ItemAttrs, []},
{xmlelement, "destroy", [],
DEls}]}]},
[{xmlelement, "item", ItemAttrs, []}, DEl]}]},
ejabberd_router:route(
jlib:jid_replace_resource(StateData#state.jid, Nick),
Info#user.jid,
@@ -2243,10 +2368,10 @@ destroy_room(DEls, StateData) ->
?FEATURE(Fiffalse)
end).
process_iq_disco_info(From, set, Lang, StateData) ->
process_iq_disco_info(_From, set, _Lang, _StateData) ->
{error, ?ERR_NOT_ALLOWED};
process_iq_disco_info(From, get, Lang, StateData) ->
process_iq_disco_info(_From, get, Lang, StateData) ->
Config = StateData#state.config,
{result, [{xmlelement, "identity",
[{"category", "conference"},
@@ -2266,13 +2391,33 @@ process_iq_disco_info(From, get, Lang, StateData) ->
"muc_moderated", "muc_unmoderated"),
?CONFIG_OPT_TO_FEATURE(Config#config.password_protected,
"muc_passwordprotected", "muc_unsecured")
], StateData}.
] ++ iq_disco_info_extras(Lang, StateData), StateData}.
-define(RFIELDT(Type, Var, Val),
{xmlelement, "field", [{"type", Type}, {"var", Var}],
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
process_iq_disco_items(From, set, Lang, StateData) ->
-define(RFIELD(Label, Var, Val),
{xmlelement, "field", [{"label", translate:translate(Lang, Label)},
{"var", Var}],
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
iq_disco_info_extras(Lang, StateData) ->
Len = length(?DICT:to_list(StateData#state.users)),
[{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "result"}],
[?RFIELDT("hidden", "FORM_TYPE",
"http://jabber.org/protocol/muc#roominfo"),
?RFIELD("Description", "muc#roominfo_description",
(StateData#state.config)#config.title),
%?RFIELD("Subject", "muc#roominfo_subject", StateData#state.subject),
?RFIELD("Number of occupants", "muc#roominfo_occupants",
integer_to_list(Len))
]}].
process_iq_disco_items(_From, set, _Lang, _StateData) ->
{error, ?ERR_NOT_ALLOWED};
process_iq_disco_items(From, get, Lang, StateData) ->
process_iq_disco_items(From, get, _Lang, StateData) ->
FAffiliation = get_affiliation(From, StateData),
FRole = get_role(From, StateData),
case ((StateData#state.config)#config.public_list == true) orelse
@@ -2282,7 +2427,7 @@ process_iq_disco_items(From, get, Lang, StateData) ->
true ->
UList =
lists:map(
fun({LJID, Info}) ->
fun({_LJID, Info}) ->
Nick = Info#user.nick,
{xmlelement, "item",
[{"jid", jlib:jid_to_string(
@@ -2314,11 +2459,11 @@ check_invitation(From, Els, StateData) ->
CanInvite = (StateData#state.config)#config.allow_user_invites
orelse (FAffiliation == admin) orelse (FAffiliation == owner),
case xml:remove_cdata(Els) of
[{xmlelement, "x", Attrs1, Els1} = XEl] ->
[{xmlelement, "x", _Attrs1, Els1} = XEl] ->
case xml:get_tag_attr_s("xmlns", XEl) of
?NS_MUC_USER ->
case xml:remove_cdata(Els1) of
[{xmlelement, "invite", Attrs2, Els2} = InviteEl] ->
[{xmlelement, "invite", Attrs2, _Els2} = InviteEl] ->
case jlib:string_to_jid(
xml:get_attr_s("to", Attrs2)) of
error ->
@@ -2378,3 +2523,16 @@ check_invitation(From, Els, StateData) ->
error
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Logging
add_to_log(Type, Data, StateData) ->
case (StateData#state.config)#config.logging of
true ->
mod_muc_log:add_to_log(
StateData#state.server_host, Type, Data,
StateData#state.jid, make_opts(StateData));
false ->
ok
end.
+13 -15
View File
@@ -5,7 +5,6 @@
%%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(mod_offline_odbc).
-author('alexey@sevcom.net').
@@ -73,9 +72,9 @@ loop(Host) ->
XML,
"');"]
end, Msgs),
case catch ejabberd_odbc:sql_query(
case catch ejabberd_odbc:sql_transaction(
Host,
["begin; ", Query, " commit"]) of
Query) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~n", [Reason]);
_ ->
@@ -212,17 +211,16 @@ pop_offline_messages(Ls, User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
EUser = ejabberd_odbc:escape(LUser),
case ejabberd_odbc:sql_query(
LServer,
["begin;"
"select username, xml from spool where username='", EUser, "'"
" order by seq;"
"delete from spool where username='", EUser, "';"
"commit"]) of
[{updated, undefined},
{selected, ["username","xml"], Rs},
{updated, _},
{updated, undefined}] ->
F = fun() ->
Result = ejabberd_odbc:sql_query_t(
["select username, xml from spool where username='", EUser, "'"
" order by seq;"]),
ejabberd_odbc:sql_query_t(
["delete from spool where username='", EUser, "';"]),
Result
end,
case ejabberd_odbc:sql_transaction(LServer,F) of
{atomic, {selected, ["username","xml"], Rs}} ->
Ls ++ lists:flatmap(
fun({_, XML}) ->
case xml_stream:parse_element(XML) of
@@ -253,5 +251,5 @@ remove_user(User, Server) ->
Username = ejabberd_odbc:escape(LUser),
ejabberd_odbc:sql_query(
LServer,
["delete from spool where username='", Username, "'"]).
["delete from spool where username='", Username, "';"]).
+6 -2
View File
@@ -362,7 +362,9 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
jlib:make_jid(LUser, ?MYNAME, ""),
jlib:make_jid(LUser, ?MYNAME, ""),
{xmlelement, "broadcast", [],
[{privacy_list, #userlist{name = Name, list = []}}]}),
[{privacy_list,
#userlist{name = Name, list = []},
Name}]}),
Res;
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
@@ -391,7 +393,9 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
jlib:make_jid(LUser, ?MYNAME, ""),
jlib:make_jid(LUser, ?MYNAME, ""),
{xmlelement, "broadcast", [],
[{privacy_list, #userlist{name = Name, list = List}}]}),
[{privacy_list,
#userlist{name = Name, list = List},
Name}]}),
Res;
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
+126 -78
View File
@@ -10,15 +10,13 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
-behaviour(gen_mod).
-export([start/2,
init/4,
loop/2,
stop/1,
system_continue/3,
system_terminate/4,
system_code_change/4]).
%% API
-export([start_link/2,
start/2,
stop/1]).
-export([delete_item/3,
set_entities/4,
@@ -28,9 +26,15 @@
get_node_config/4,
set_node_config/4]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(state, {host}).
-define(DICT, dict).
-define(MAXITEMS, 20).
-define(MAX_PAYLOAD_SIZE, 100000).
@@ -45,60 +49,35 @@
-record(item, {id, publisher, payload}).
-define(PROCNAME, ejabberd_mod_pubsub).
start(Host, Opts) ->
mnesia:create_table(pubsub_node,
[{disc_only_copies, [node()]},
{attributes, record_info(fields, pubsub_node)}]),
MyHost = gen_mod:get_opt(host, Opts, "pubsub." ++ Host),
update_table(MyHost),
mnesia:add_table_index(pubsub_node, host_parent),
ServedHosts = gen_mod:get_opt(served_hosts, Opts, []),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
proc_lib:spawn_link(?MODULE, init,
[MyHost, Host, ServedHosts, self()])).
-define(MYJID, #jid{user = "", server = Host, resource = "",
luser = "", lserver = Host, lresource = ""}).
init(Host, ServerHost, ServedHosts, Parent) ->
ejabberd_router:register_route(Host),
create_new_node(Host, ["pubsub"], ?MYJID),
create_new_node(Host, ["pubsub", "nodes"], ?MYJID),
create_new_node(Host, ["home"], ?MYJID),
create_new_node(Host, ["home", ServerHost], ?MYJID),
lists:foreach(fun(H) ->
create_new_node(Host, ["home", H], ?MYJID)
end, ServedHosts),
ets:new(gen_mod:get_module_proc(Host, pubsub_presence), [set, named_table]),
loop(Host, Parent).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
loop(Host, Parent) ->
receive
{route, From, To, Packet} ->
case catch do_route(To#jid.lserver, From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]);
_ ->
ok
end,
loop(Host, Parent);
{room_destroyed, Room} ->
ets:delete(muc_online_room, Room),
loop(Host, Parent);
stop ->
ejabberd_router:unregister_route(Host),
ok;
reload ->
?MODULE:loop(Host, Parent);
{system, From, Request} ->
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], Host);
_ ->
loop(Host, Parent)
end.
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
temporary,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
%%% API functions
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:stop_child(ejabberd_sup, Proc).
delete_item(From, Node, ItemID) ->
delete_item(get_host(), From, Node, ItemID).
@@ -124,8 +103,97 @@ get_host() ->
timeout
end.
%%% Internal functions
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([ServerHost, Opts]) ->
mnesia:create_table(pubsub_node,
[{disc_only_copies, [node()]},
{attributes, record_info(fields, pubsub_node)}]),
Host = gen_mod:get_opt(host, Opts, "pubsub." ++ ServerHost),
update_table(Host),
mnesia:add_table_index(pubsub_node, host_parent),
ServedHosts = gen_mod:get_opt(served_hosts, Opts, []),
ejabberd_router:register_route(Host),
create_new_node(Host, ["pubsub"], ?MYJID),
create_new_node(Host, ["pubsub", "nodes"], ?MYJID),
create_new_node(Host, ["home"], ?MYJID),
create_new_node(Host, ["home", ServerHost], ?MYJID),
lists:foreach(fun(H) ->
create_new_node(Host, ["home", H], ?MYJID)
end, ServedHosts),
ets:new(gen_mod:get_module_proc(Host, pubsub_presence),
[set, named_table]),
{ok, #state{host = Host}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(stop, _From, State) ->
{stop, normal, ok, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) ->
case catch do_route(To#jid.lserver, From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]);
_ ->
ok
end,
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, State) ->
ejabberd_router:unregister_route(State#state.host),
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
do_route(Host, From, To, Packet) ->
{xmlelement, Name, Attrs, Els} = Packet,
case To of
@@ -236,14 +304,6 @@ do_route(Host, From, To, Packet) ->
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
Proc ! stop,
{wait, Proc}.
node_to_string(Node) ->
string:strip(lists:flatten(lists:map(fun(S) -> [S, "/"] end, Node)),
right, $/).
@@ -334,7 +394,7 @@ iq_get_vcard(Lang) ->
[{xmlcdata, translate:translate(
Lang,
"ejabberd pub/sub module\n"
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
"Copyright (c) 2003-2006 Alexey Shchepin")}]}].
iq_pubsub(Host, From, Type, SubEl) ->
@@ -1261,18 +1321,6 @@ broadcast_config_notification(Host, Node, Lang) ->
system_continue(Parent, _, State) ->
loop(State, Parent).
system_terminate(Reason, Parent, _, State) ->
exit(Reason).
system_code_change(State, _Mod, Ver, _Extra) ->
{ok, State}.
iq_pubsub_owner(Host, From, Type, Lang, SubEl) ->
{xmlelement, _, _, SubEls} = SubEl,
case xml:remove_cdata(SubEls) of
+94 -103
View File
@@ -25,14 +25,7 @@
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(roster, {user,
jid,
name = "",
subscription = none,
ask = none,
groups = []
}).
-include("mod_roster.hrl").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
@@ -74,8 +67,8 @@ stop(Host) ->
process_iq(From, To, IQ) ->
#iq{sub_el = SubEl} = IQ,
#jid{lserver = LServer} = From,
case ?MYNAME of
LServer ->
case lists:member(LServer, ?MYHOSTS) of
true ->
ResIQ = process_local_iq(From, To, IQ),
ejabberd_router:route(From, From,
jlib:iq_to_xml(ResIQ)),
@@ -89,8 +82,8 @@ process_iq(From, To, IQ) ->
process_iq(From, To, IQ) ->
#iq{sub_el = SubEl} = IQ,
#jid{lserver = LServer} = From,
case ?MYNAME of
LServer ->
case lists:member(LServer, ?MYHOSTS) of
true ->
process_local_iq(From, To, IQ);
_ ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
@@ -108,9 +101,22 @@ process_local_iq(From, To, #iq{type = Type} = IQ) ->
process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) ->
process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) ->
LUser = From#jid.luser,
LServer = From#jid.lserver,
US = {LUser, LServer},
case catch ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US]) of
Items when is_list(Items) ->
XItems = lists:map(fun item_to_xml/1, Items),
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_ROSTER}],
XItems}]};
_ ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
end.
get_user_roster(Acc, {LUser, LServer}) ->
Username = ejabberd_odbc:escape(LUser),
case catch ejabberd_odbc:sql_query(
LServer,
@@ -130,9 +136,9 @@ process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) ->
_ ->
[]
end,
XItems = lists:flatmap(
RItems = lists:flatmap(
fun(I) ->
case raw_to_record(I) of
case raw_to_record(LServer, I) of
error ->
[];
R ->
@@ -143,34 +149,7 @@ process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) ->
(_) ->
[]
end, JIDGroups),
[item_to_xml(R#roster{groups = Groups})]
end
end, Items),
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_ROSTER}],
XItems}]};
_ ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
end.
get_user_roster(Acc, {LUser, LServer}) ->
Username = ejabberd_odbc:escape(LUser),
case catch ejabberd_odbc:sql_query(
LServer,
["select username, jid, nick, subscription, ask, "
"server, subscribe, type from rosterusers "
"where username='", Username, "'"]) of
{selected, ["username", "jid", "nick", "subscription", "ask",
"server", "subscribe", "type"],
Items} when is_list(Items) ->
RItems = lists:flatmap(
fun(I) ->
case raw_to_record(I) of
error ->
[];
R ->
[R]
[R#roster{groups = Groups}]
end
end, Items),
RItems ++ Acc;
@@ -241,12 +220,15 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
"and jid='", SJID, "'"]),
Item = case Res of
[] ->
#roster{user = LUser,
#roster{usj = {LUser, LServer, LJID},
us = {LUser, LServer},
jid = LJID};
[I] ->
(raw_to_record(I))#roster{user = LUser,
jid = JID,
name = ""}
(raw_to_record(LServer, I))#roster{
usj = {LUser, LServer, LJID},
us = {LUser, LServer},
jid = LJID,
name = ""}
end,
Item1 = process_item_attrs(Item, Attrs),
Item2 = process_item_els(Item1, Els),
@@ -255,29 +237,35 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
ejabberd_odbc:sql_query_t(
["delete from rosterusers "
" where username='", Username, "' "
" and jid='", SJID, "';"
"delete from rostergroups "
" and jid='", SJID, "';"]),
ejabberd_odbc:sql_query_t(
["delete from rostergroups "
" where username='", Username, "' "
" and jid='", SJID, "'"]);
" and jid='", SJID, "';"]);
_ ->
ItemVals = record_to_string(Item2),
ItemGroups = groups_to_string(Item2),
ejabberd_odbc:sql_query_t(
["delete from rosterusers "
" where username='", Username, "' "
" and jid='", SJID, "';"
"insert into rosterusers("
" and jid='", SJID, "';"]),
ejabberd_odbc:sql_query_t(
["insert into rosterusers("
" username, jid, nick, "
" subscription, ask, "
" server, subscribe, type) "
" values ", ItemVals, ";"
"delete from rostergroups "
" values ", ItemVals, ";"]),
ejabberd_odbc:sql_query_t(
["delete from rostergroups "
" where username='", Username, "' "
" and jid='", SJID, "';",
[["insert into rostergroups("
" username, jid, grp) "
" values ", ItemGroup, ";"] ||
ItemGroup <- ItemGroups]])
" and jid='", SJID, "';"]),
lists:foreach(fun(ItemGroup) ->
ejabberd_odbc:sql_query_t(
["insert into rostergroups("
" username, jid, grp) "
" values ", ItemGroup, ";"])
end,
ItemGroups)
end,
{Item, Item2}
end,
@@ -333,7 +321,7 @@ process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
error ->
process_item_attrs(Item, Attrs);
JID1 ->
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
process_item_attrs(Item#roster{jid = JID}, Attrs)
end;
"name" ->
@@ -421,25 +409,25 @@ get_subscription_lists(_, User, Server) ->
{selected, ["username", "jid", "nick", "subscription", "ask",
"server", "subscribe", "type"],
Items} when is_list(Items) ->
fill_subscription_lists(Items, [], []);
fill_subscription_lists(LServer, Items, [], []);
_ ->
{[], []}
end.
fill_subscription_lists([RawI | Is], F, T) ->
I = raw_to_record(RawI),
fill_subscription_lists(LServer, [RawI | Is], F, T) ->
I = raw_to_record(LServer, RawI),
J = I#roster.jid,
case I#roster.subscription of
both ->
fill_subscription_lists(Is, [J | F], [J | T]);
fill_subscription_lists(LServer, Is, [J | F], [J | T]);
from ->
fill_subscription_lists(Is, [J | F], T);
fill_subscription_lists(LServer, Is, [J | F], T);
to ->
fill_subscription_lists(Is, F, [J | T]);
fill_subscription_lists(LServer, Is, F, [J | T]);
_ ->
fill_subscription_lists(Is, F, T)
fill_subscription_lists(LServer, Is, F, T)
end;
fill_subscription_lists([], F, T) ->
fill_subscription_lists(_LServer, [], F, T) ->
{F, T}.
ask_to_pending(subscribe) -> out;
@@ -466,17 +454,17 @@ process_subscription(Direction, User, Server, JID1, Type) ->
["select username, jid, nick, subscription, ask, "
"server, subscribe, type from rosterusers "
"where username='", Username, "' "
"and jid='", SJID, "'"]) of
"and jid='", SJID, "';"]) of
{selected,
["username", "jid", "nick", "subscription", "ask",
"server", "subscribe", "type"],
[I]} ->
R = raw_to_record(I),
R = raw_to_record(LServer, I),
Groups =
case ejabberd_odbc:sql_query_t(
["select grp from rostergroups "
"where username='", Username, "' "
"and jid='", SJID, "'"]) of
"and jid='", SJID, "';"]) of
{selected, ["grp"], JGrps} when is_list(JGrps) ->
[JGrp || {JGrp} <- JGrps];
_ ->
@@ -487,7 +475,8 @@ process_subscription(Direction, User, Server, JID1, Type) ->
["username", "jid", "nick", "subscription", "ask",
"server", "subscribe", "type"],
[]} ->
#roster{user = LUser,
#roster{usj = {LUser, LServer, LJID},
us = {LUser, LServer},
jid = LJID}
end,
NewState = case Direction of
@@ -518,12 +507,13 @@ process_subscription(Direction, User, Server, JID1, Type) ->
ejabberd_odbc:sql_query_t(
["delete from rosterusers "
" where username='", Username, "' "
" and jid='", SJID, "';"
"insert into rosterusers("
" and jid='", SJID, "';"]),
ejabberd_odbc:sql_query_t(
["insert into rosterusers("
" username, jid, nick, "
" subscription, ask, "
" server, subscribe, type) "
" values ", ItemVals]),
" values ", ItemVals, ";"]),
{{push, NewItem}, AutoReply}
end
end,
@@ -659,9 +649,10 @@ remove_user(User, Server) ->
fun() ->
ejabberd_odbc:sql_query_t(
["delete from rosterusers "
" where username='", Username, "';"
"delete from rostergroups "
" where username='", Username, "'"])
" where username='", Username, "';"]),
ejabberd_odbc:sql_query_t(
["delete from rostergroups "
" where username='", Username, "';"])
end),
ok.
@@ -671,13 +662,11 @@ set_items(User, Server, SubEl) ->
{xmlelement, _Name, _Attrs, Els} = SubEl,
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
catch ejabberd_odbc:sql_query(
catch ejabberd_odbc:transaction(
LServer,
["begin;",
lists:map(fun(El) ->
process_item_set_t(LUser, LServer, El)
end, Els),
"commit"]).
end, Els)).
process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
@@ -689,32 +678,33 @@ process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
LJID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
Item = #roster{user = LUser,
Item = #roster{usj = {LUser, LServer, LJID},
us = {LUser, LServer},
jid = LJID},
Item1 = process_item_attrs_ws(Item, Attrs),
Item2 = process_item_els(Item1, Els),
case Item2#roster.subscription of
remove ->
["delete from rosterusers "
[["delete from rosterusers "
" where username='", Username, "' "
" and jid='", SJID, "';"
"delete from rostergroups "
" and jid='", SJID, "';"],
["delete from rostergroups "
" where username='", Username, "' "
" and jid='", SJID, "';"];
" and jid='", SJID, "';"]];
_ ->
ItemVals = record_to_string(Item1),
ItemGroups = groups_to_string(Item2),
["delete from rosterusers "
[["delete from rosterusers "
" where username='", Username, "' "
" and jid='", SJID, "';"
"insert into rosterusers("
" username, jid, nick, "
" subscription, ask, "
" server, subscribe, type) "
" values ", ItemVals, ";"
"delete from rostergroups "
" where username='", Username, "' "
" and jid='", SJID, "';",
" and jid='", SJID, "';"],
["insert into rosterusers("
" username, jid, nick, "
" subscription, ask, "
" server, subscribe, type) "
" values ", ItemVals, ";"],
["delete from rostergroups "
" where username='", Username, "' "
" and jid='", SJID, "';"],
[["insert into rostergroups("
" username, jid, grp) "
" values ", ItemGroup, ";"] ||
@@ -731,7 +721,7 @@ process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) ->
error ->
process_item_attrs_ws(Item, Attrs);
JID1 ->
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
process_item_attrs_ws(Item#roster{jid = JID}, Attrs)
end;
"name" ->
@@ -834,8 +824,8 @@ get_jid_info(_, User, Server, JID) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
raw_to_record({User, SJID, Nick, SSubscription, SAsk,
_SServer, _SSubscribe, _SType}) ->
raw_to_record(LServer, {User, SJID, Nick, SSubscription, SAsk,
_SServer, _SSubscribe, _SType}) ->
case jlib:string_to_jid(SJID) of
error ->
error;
@@ -855,14 +845,15 @@ raw_to_record({User, SJID, Nick, SSubscription, SAsk,
"I" -> in;
_ -> none
end,
#roster{user = User,
#roster{usj = {User, LServer, LJID},
us = {User, LServer},
jid = LJID,
name = Nick,
subscription = Subscription,
ask = Ask}
end.
record_to_string(#roster{user = User,
record_to_string(#roster{us = {User, _Server},
jid = JID,
name = Name,
subscription = Subscription,
@@ -892,7 +883,7 @@ record_to_string(#roster{user = User,
"'", SAsk, "',"
"'N', '', 'item')"].
groups_to_string(#roster{user = User,
groups_to_string(#roster{us = {User, _Server},
jid = JID,
groups = Groups}) ->
Username = ejabberd_odbc:escape(User),
+4 -5
View File
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : mod_service_log.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Manage announce messages
%%% Purpose : Copy user messages to logger service
%%% Created : 24 Aug 2003 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
@@ -34,14 +34,13 @@ stop(Host) ->
ok.
log_user_send(From, To, Packet) ->
log_packet(From, To, Packet).
log_packet(From, To, Packet, From#jid.lserver).
log_user_receive(_JID, From, To, Packet) ->
log_packet(From, To, Packet).
log_packet(From, To, Packet, To#jid.lserver).
log_packet(From, To, {xmlelement, Name, Attrs, Els}) ->
Host = From#jid.lserver,
log_packet(From, To, {xmlelement, Name, Attrs, Els}, Host) ->
Loggers = gen_mod:get_module_opt(Host, ?MODULE, loggers, []),
ServerJID = #jid{user = "", server = Host, resource = "",
luser = "", lserver = Host, lresource = ""},
+6 -6
View File
@@ -147,7 +147,7 @@ process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
translate:translate(
Lang,
"Erlang Jabber Server\n"
"Copyright (c) 2002-2005 Alexey Shchepin")}]},
"Copyright (c) 2002-2006 Alexey Shchepin")}]},
{xmlelement, "BDAY", [],
[{xmlcdata, "2002-11-16"}]}
]}]}
@@ -281,7 +281,7 @@ set_vcard(User, LServer, VCARD) ->
?TLFIELD("text-single", "Birthday", "bday"),
?TLFIELD("text-single", "Country", "ctry"),
?TLFIELD("text-single", "City", "locality"),
?TLFIELD("text-single", "email", "email"),
?TLFIELD("text-single", "Email", "email"),
?TLFIELD("text-single", "Organization Name", "orgname"),
?TLFIELD("text-single", "Organization Unit", "orgunit")
]}]).
@@ -416,7 +416,7 @@ iq_get_vcard(Lang) ->
[{xmlcdata, translate:translate(
Lang,
"ejabberd vCard module\n"
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
"Copyright (c) 2003-2006 Alexey Shchepin")}]}].
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
find_xdata_el1(SubEls).
@@ -439,10 +439,10 @@ find_xdata_el1([_ | Els]) ->
search_result(Lang, JID, ServerHost, Data) ->
[{xmlelement, "title", [],
[{xmlcdata, translate:translate(Lang, "Results of search in ") ++
[{xmlcdata, translate:translate(Lang, "Search Results for ") ++
jlib:jid_to_string(JID)}]},
{xmlelement, "reported", [],
[?LFIELD("JID", "jid"),
[?LFIELD("Jabber ID", "jid"),
?LFIELD("Full Name", "fn"),
?LFIELD("Name", "given"),
?LFIELD("Middle Name", "middle"),
@@ -451,7 +451,7 @@ search_result(Lang, JID, ServerHost, Data) ->
?LFIELD("Birthday", "bday"),
?LFIELD("Country", "ctry"),
?LFIELD("City", "locality"),
?LFIELD("email", "email"),
?LFIELD("Email", "email"),
?LFIELD("Organization Name", "orgname"),
?LFIELD("Organization Unit", "orgunit")
]}] ++ lists:map(fun record_to_item/1, search(ServerHost, Data)).
+6 -6
View File
@@ -108,7 +108,7 @@ process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
translate:translate(
Lang,
"Erlang Jabber Server\n"
"Copyright (c) 2002-2005 Alexey Shchepin")}]},
"Copyright (c) 2002-2006 Alexey Shchepin")}]},
{xmlelement, "BDAY", [],
[{xmlcdata, "2002-11-16"}]}
]}]}
@@ -239,7 +239,7 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
?TLFIELD("text-single", "Birthday", "bday"),
?TLFIELD("text-single", "Country", "ctry"),
?TLFIELD("text-single", "City", "locality"),
?TLFIELD("text-single", "email", "email"),
?TLFIELD("text-single", "Email", "email"),
?TLFIELD("text-single", "Organization Name", "orgname"),
?TLFIELD("text-single", "Organization Unit", "orgunit")
]}]).
@@ -374,7 +374,7 @@ iq_get_vcard(Lang) ->
[{xmlcdata, translate:translate(
Lang,
"ejabberd vCard module\n"
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
"Copyright (c) 2003-2006 Alexey Shchepin")}]}].
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
find_xdata_el1(SubEls).
@@ -397,10 +397,10 @@ find_xdata_el1([_ | Els]) ->
search_result(Lang, JID, ServerHost, Data) ->
[{xmlelement, "title", [],
[{xmlcdata, translate:translate(Lang, "Results of search in ") ++
[{xmlcdata, translate:translate(Lang, "Search Results for ") ++
jlib:jid_to_string(JID)}]},
{xmlelement, "reported", [],
[?LFIELD("JID", "jid"),
[?LFIELD("Jabber ID", "jid"),
?LFIELD("Full Name", "fn"),
?LFIELD("Given Name", "given"),
?LFIELD("Middle Name", "middle"),
@@ -409,7 +409,7 @@ search_result(Lang, JID, ServerHost, Data) ->
?LFIELD("Birthday", "bday"),
?LFIELD("Country", "ctry"),
?LFIELD("City", "locality"),
?LFIELD("email", "email"),
?LFIELD("Email", "email"),
?LFIELD("Organization Name", "orgname"),
?LFIELD("Organization Unit", "orgunit")
]}] ++ lists:map(fun(E) ->
+35 -50
View File
@@ -111,7 +111,7 @@ process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
translate:translate(
Lang,
"Erlang Jabber Server\n"
"Copyright (c) 2002-2005 Alexey Shchepin")}]},
"Copyright (c) 2002-2006 Alexey Shchepin")}]},
{xmlelement, "BDAY", [],
[{xmlcdata, "2002-11-16"}]}
]}]}
@@ -131,12 +131,11 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
end;
get ->
#jid{luser = LUser, lserver = LServer} = To,
US = {LUser, LServer},
Username = ejabberd_odbc:escape(LUser),
case catch ejabberd_odbc:sql_query(
LServer,
["select vcard from vcard "
"where username='", Username, "'"]) of
"where username='", Username, "';"]) of
{selected, ["vcard"], [{SVCARD}]} ->
case xml_stream:parse_element(SVCARD) of
{error, _Reason} ->
@@ -186,8 +185,6 @@ set_vcard(User, LServer, VCARD) ->
LOrgName = stringprep:tolower(OrgName),
LOrgUnit = stringprep:tolower(OrgUnit),
US = {LUser, LServer},
if
(LUser == error) or
(LFN == error) or
@@ -231,32 +228,30 @@ set_vcard(User, LServer, VCARD) ->
SOrgUnit = ejabberd_odbc:escape(OrgUnit),
SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
ejabberd_odbc:sql_query(
ejabberd_odbc:sql_transaction(
LServer,
["begin;"
"delete from vcard where username='", LUsername, "';"
"insert into vcard(username, vcard) "
"values ('", LUsername, "', '", SVCARD, "');"
"delete from vcard_search where lusername='", LUsername, "';"
"insert into vcard_search("
" username, lusername, fn, lfn, family, lfamily,"
" given, lgiven, middle, lmiddle, nickname, lnickname,"
" bday, lbday, ctry, lctry, locality, llocality,"
" email, lemail, orgname, lorgname, orgunit, lorgunit)"
"values (",
" '", Username, "', '", LUsername, "',"
" '", SFN, "', '", SLFN, "',"
" '", SFamily, "', '", SLFamily, "',"
" '", SGiven, "', '", SLGiven, "',"
" '", SMiddle, "', '", SLMiddle, "',"
" '", SNickname, "', '", SLNickname, "',"
" '", SBDay, "', '", SLBDay, "',"
" '", SCTRY, "', '", SLCTRY, "',"
" '", SLocality, "', '", SLLocality, "',"
" '", SEMail, "', '", SLEMail, "',"
" '", SOrgName, "', '", SLOrgName, "',"
" '", SOrgUnit, "', '", SLOrgUnit, "');"
"commit"])
[["delete from vcard where username='", LUsername, "';"],
["insert into vcard(username, vcard) "
"values ('", LUsername, "', '", SVCARD, "');"],
["delete from vcard_search where lusername='", LUsername, "';"],
["insert into vcard_search("
" username, lusername, fn, lfn, family, lfamily,"
" given, lgiven, middle, lmiddle, nickname, lnickname,"
" bday, lbday, ctry, lctry, locality, llocality,"
" email, lemail, orgname, lorgname, orgunit, lorgunit)"
"values (",
" '", Username, "', '", LUsername, "',"
" '", SFN, "', '", SLFN, "',"
" '", SFamily, "', '", SLFamily, "',"
" '", SGiven, "', '", SLGiven, "',"
" '", SMiddle, "', '", SLMiddle, "',"
" '", SNickname, "', '", SLNickname, "',"
" '", SBDay, "', '", SLBDay, "',"
" '", SCTRY, "', '", SLCTRY, "',"
" '", SLocality, "', '", SLLocality, "',"
" '", SEMail, "', '", SLEMail, "',"
" '", SOrgName, "', '", SLOrgName, "',"
" '", SOrgUnit, "', '", SLOrgUnit, "');"]])
end.
-define(TLFIELD(Type, Label, Var),
@@ -286,14 +281,11 @@ set_vcard(User, LServer, VCARD) ->
?TLFIELD("text-single", "Birthday", "bday"),
?TLFIELD("text-single", "Country", "ctry"),
?TLFIELD("text-single", "City", "locality"),
?TLFIELD("text-single", "email", "email"),
?TLFIELD("text-single", "Email", "email"),
?TLFIELD("text-single", "Organization Name", "orgname"),
?TLFIELD("text-single", "Organization Unit", "orgunit")
]}]).
do_route(ServerHost, From, To, Packet) ->
#jid{user = User, resource = Resource} = To,
if
@@ -421,7 +413,7 @@ iq_get_vcard(Lang) ->
[{xmlcdata, translate:translate(
Lang,
"ejabberd vCard module\n"
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
"Copyright (c) 2003-2006 Alexey Shchepin")}]}].
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
find_xdata_el1(SubEls).
@@ -444,10 +436,10 @@ find_xdata_el1([_ | Els]) ->
search_result(Lang, JID, ServerHost, Data) ->
[{xmlelement, "title", [],
[{xmlcdata, translate:translate(Lang, "Results of search in ") ++
[{xmlcdata, translate:translate(Lang, "Search Results for ") ++
jlib:jid_to_string(JID)}]},
{xmlelement, "reported", [],
[?LFIELD("JID", "jid"),
[?LFIELD("Jabber ID", "jid"),
?LFIELD("Full Name", "fn"),
?LFIELD("Name", "given"),
?LFIELD("Middle Name", "middle"),
@@ -456,7 +448,7 @@ search_result(Lang, JID, ServerHost, Data) ->
?LFIELD("Birthday", "bday"),
?LFIELD("Country", "ctry"),
?LFIELD("City", "locality"),
?LFIELD("email", "email"),
?LFIELD("Email", "email"),
?LFIELD("Organization Name", "orgname"),
?LFIELD("Organization Unit", "orgunit")
]}] ++ lists:map(fun(R) -> record_to_item(ServerHost, R) end,
@@ -514,7 +506,7 @@ search(LServer, Data) ->
["select username, fn, family, given, middle, "
" nickname, bday, ctry, locality, "
" email, orgname, orgunit from vcard_search ",
MatchSpec, Limit]) of
MatchSpec, Limit, ";"]) of
{selected, ["username", "fn", "family", "given", "middle",
"nickname", "bday", "ctry", "locality",
"email", "orgname", "orgunit"],
@@ -564,12 +556,7 @@ make_val(Match, Field, Val) ->
case lists:suffix("*", Val) of
true ->
Val1 = lists:sublist(Val, length(Val) - 1),
Val2 = lists:flatten([case C of
$_ -> "\\_";
$% -> "\\%";
_ -> C
end || C <- Val1]),
SVal = ejabberd_odbc:escape(Val2 ++ "%"),
SVal = ejabberd_odbc:escape_like(Val1) ++ "%",
[Field, " LIKE '", SVal, "'"];
_ ->
SVal = ejabberd_odbc:escape(Val),
@@ -660,11 +647,9 @@ remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
Username = ejabberd_odbc:escape(LUser),
ejabberd_odbc:sql_query(
ejabberd_odbc:sql_transaction(
LServer,
["begin;"
"delete from vcard where username='", Username, "';"
"delete from vcard_search where lusername='", Username, "';"
"commit"]).
[["delete from vcard where username='", Username, "';"],
["delete from vcard_search where lusername='", Username, "';"]]).
+130 -77
View File
@@ -38,27 +38,23 @@
{"Action on user", "Aktion auf Benutzer"}.
{"Edit Properties", "Einstellungen ändern"}.
{"Remove User", "Benutzer löschen"}.
{"DB Tables Configuration at ", "DB Tabellenkonfiguration auf "}.
% mod_disco.erl
{"Configuration", "Konfiguration"}.
{"Online Users", "Online Benutzer"}.
{"All Users", "Alle Benutzer"}.
{"Outgoing S2S connections", "Rausgehende S2S Verbindungen"}.
{"To ~s", "An ~s"}.
{"From ~s", "Vom ~s"}.
{"From ~s", "Von ~s"}.
{"Running Nodes", "Aktive Knoten"}.
{"Stopped Nodes", "Inaktive Knoten"}.
{"Host Name", "Hostname"}.
{"Access Control Lists", "Zugangskontrolllisten"}.
{"Access Rules", "Zugangsregeln"}.
{"Remove Users", "Benutzer löschen"}.
{"DB", "DB"}.
{"Modules", "Module"}.
{"Start Modules", "Module starten"}.
{"Stop Modules", "Module stoppen"}.
{"Backup Management", "Datensicherungsmanagement"}.
{"Import users from jabberd1.4 spool files", "Benutzer von jabberd1.4 spool Dateien importieren"}.
{"Backup", "Sicherung"}.
{"Restore", "Wiederherstellung"}.
{"Dump to Text File", "Ausgabe in Textdatei"}.
@@ -70,15 +66,14 @@
"Wählen Sie zum Registrieren einen Benutzernamen und ein Passwort"}.
% mod_vcard.erl
{"ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin", "ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"ejabberd vCard module\nCopyright (c) 2003-2006 Alexey Shchepin", "ejabberd vCard module\nCopyright (c) 2003-2006 Alexey Shchepin"}.
{"You need an x:data capable client to search",
"Sie brauchen einen x:data kompatiblen Client um zu suchen"}.
"Sie brauchen einen x:data kompatiblen Client um suchen zu können"}.
{"Search users in ", "Benutzer suchen in "}.
{"Fill in fields to search for any matching Jabber User",
"Felder ausfüllen, um nach passenden Jabber Benutzern zu suchen"}.
{"Results of search in ", "Ergebnisse der Suche in "}.
{"User", "User"}.
{"User", "Benutzer"}.
{"Full Name", "Ganzer Name"}.
{"Name", "Vorname"}.
{"Middle Name", "Mittelname"}.
@@ -87,12 +82,11 @@
{"Birthday", "Geburtsdatum"}.
{"Country", "Land"}.
{"City", "Stadt"}.
{"email", "E-Mail"}.
{"Organization Name", "Firmenname"}.
{"Organization Unit", "Organisationseinheit"}.
{"Organization Unit", "Abteilung"}.
% mod_pubsub/mod_pubsub.erl
{"ejabberd pub/sub module\nCopyright (c) 2003-2005 Alexey Shchepin", "ejabberd pub/sub module\nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"ejabberd pub/sub module\nCopyright (c) 2003-2006 Alexey Shchepin", "ejabberd pub/sub module\nCopyright (c) 2003-2006 Alexey Shchepin"}.
{[], " "}.
{"Node Creator", "Knoten Erschaffer"}.
{"Deliver payloads with event notifications", "Nutzlast mit Benachrichtigungen versenden"}.
@@ -114,12 +108,12 @@
"Sie brauchen einen für x:data geeigneten Client, um sich mit dem Spitznamen zu registrieren"}.
{"Nickname Registration at ", "Registrieren des Spitznamens "}.
{"Enter nickname you want to register", "Geben Sie den zu registrierenden Spitznamen ein"}.
{"ejabberd MUC module\nCopyright (c) 2003-2005 Alexey Shchepin", "ejabberd MUC module\nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"ejabberd MUC module\nCopyright (c) 2003-2006 Alexey Shchepin", "ejabberd MUC module\nCopyright (c) 2003-2006 Alexey Shchepin"}.
{"Only service administrators are allowed to send service messages",
"Nur Service Administratoren sind berechtigt, Servicenachrichten zu lesen"}.
"Nur Service Administratoren sind berechtigt, Servicenachrichten zu senden"}.
{"Conference room does not exist", "Konferenzraum existiert nicht"}.
{"Access denied by service policy", "Zutritt verweigert von der Service Policy"}.
{"You must fill in field \"nick\" in the form", "Sie müssen etwas ins Feld \"nick\" eintragen"}.
{"You must fill in field \"Nickname\" in the form", "Sie müssen etwas ins Feld \"Nickname\" eintragen"}.
{"Specified nickname is already registered", "Der angegebene Name ist bereits vergeben"}.
{"Room creation is denied by service policy", "Anlegen des Raumes aufgrund von Servereinstellungen verweigert"}.
@@ -129,25 +123,7 @@
"Sie brauchen einen für x:data geeigneten Client um den Raum zu konfigurieren"}.
{"Configuration for ", "Konfiguration für "}.
{"Room title", "Raumname"}.
{"Allow users to change subject?", "Benutzer dürfen das Thema ändern?"}.
{"Allow users to query other users?",
"Benutzer dürfen andere Benutzer anfragen(query)?"}.
{"Allow users to send private messages?",
"Benutzer dürfen private Nachrichten verschicken?"}.
{"Make room public searchable?", "Den Raum öffentlich dursuchbar machen?"}.
{"Make participants list public?", "Die Teilnehmerliste öffentlich machen?"}.
{"Make room persistent?", "Den Raum persistent machen?"}.
{"Make room moderated?", "Den Raum moderiert machen?"}.
{"Default users as members?",
"Standardbenutzer als Mitglieder ansehen?"}.
{"Make room members only?",
"Den Raum nur für Mitglieder zugänglich machen?"}.
{"Allow users to send invites?",
"Benutzer dürfen Einladungen senden?"}.
{"Make room password protected?", "Den Raum passwort geschützt machen?"}.
{"Password", "Passwort"}.
{"Make room anonymous?", "Den Raum anonym machen?"}.
{"Enable logging?", "Nachrichten loggen?"}.
{"Only moderators and participants are allowed to change subject in this room",
"Nur Moderatoren und Teilnehmer dürfen das Thema in diesem Raum ändern"}.
{"Only moderators are allowed to change subject in this room",
@@ -184,7 +160,7 @@
{"private, ", "privat"}.
% mod_irc/mod_irc.erl
{"ejabberd IRC module\nCopyright (c) 2003-2005 Alexey Shchepin", "ejabberd IRC module\nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"ejabberd IRC module\nCopyright (c) 2003-2006 Alexey Shchepin", "ejabberd IRC module\nCopyright (c) 2003-2006 Alexey Shchepin"}.
{"You need an x:data capable client to configure mod_irc settings",
"Sie brauchen einen x:data kompatiblen Client um die mod_irc Einstellungen zu konfigurieren"}.
{"Registration in mod_irc for ", "Registrierung in mod_irc für "}.
@@ -197,85 +173,51 @@
{"If you want to specify different encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\"}'. By default this service use \"~s\" encoding.", "Wenn Sie verschiedene Verschlüsselungen für IRC Server angeben, schreiben Sie diese im Format '{\"irc server\", \"encoding\"}'. Standardmäßig benutzt der Dienst die \"~s\" Verschlüsselung"}.
% web/ejabberd_web_admin.erl
{"ejabberd administration", "Ejabberd Administration"}.
{"Users", "Benutzer"}.
{"Nodes", "Knoten(nodes)"}.
{"Statistics", "Statistik"}.
{"submitted", "bestätigt"}.
{"bad format", "ungültiges Format"}.
{"ejabberd access control lists configuration", "Ejabberd access control lists Konfiguration"}.
{"Delete Selected", "Lösche Markiertes"}.
{"Submit", "Bestätigen"}.
{"ejabberd access rules configuration", "Ejabberd Zutrittsregeln Konfiguration"}.
{"~s access rule configuration", "~s Zurtrittsregel Konfiguration"}.
{"ejabberd users", "Ejabberd Benutzer"}.
{"ejabberd stats", "Ejabberd Statistik"}.
{"Node not found", "Knoten nicht gefunden"}.
{"Add New", "Neuen hinzufügen"}.
{"Registered users", "Registrierte Benutzer"}.
{"Online users", "Online Benutzer"}.
{"Outgoing S2S servers", "Rausgehende S2S Server"}.
{"Change Password", "Passwort ändern"}.
{"Connected Resources:", "Verbundene Resourcen"}.
{"Password:", "Passwort:"}.
{"None", "Keine"}.
{"Node ", "Knoten "}.
{"DB Management", "DB Management"}.
{"Listened Ports Management", "Ports Management"}.
{"Restart", "Neustart"}.
{"Stop", "Stop"}.
{"RPC call error", "RPC-Aufruf Fehler"}.
{"DB Tables at ", "DB Tabellen bei "}.
{"Name", "Name"}.
{"Storage Type", "Speichertyp"}.
{"Size", "Größe"}.
{"Memory", "Speicher"}.
{"Backup Management at ", "Backup Management bei "}.
{"Store a backup in a file", "Die Sicherung in eine Datei legen "}.
{"OK", "OK"}.
{"Restore a backup from a file", "Die Sicherung aus einer Datei wiederherstellen"}.
{"Install a database fallback from a file", "Eine Sicherungsdatenbank aus einer Datei installieren"}.
{"Dump a database in a text file", "Ausgabe der Datenbank in eine Textdatei"}.
{"Restore a database from a text file", "Datenbank aus einer Textdatei wiederherstellen"}.
{"Listened Ports at ", "Listened Ports at "}.
{"~p statistics", "~p Statistiken"}.
{"Uptime", "Betriebszeit"}.
{"CPU Time", "CPU Zeit"}.
{"Transactions commited", "abgeschlossene Vorgänge"}.
{"Transactions aborted", "abgebrochene Vorgänge"}.
{"Transactions restarted", "neugestartete Vorgänge"}.
{"Transactions logged", "geloggte Vorgänge"}.
{"Listened Ports at ", "Überwachte Ports "}.
{"Port", "Port"}.
{"Module", "Modul"}.
{"Options", "Optionen"}.
{"Update", "Aktualisieren"}.
{"Delete", "Löschen"}.
{"Add User", "Benutzer hinzufügen"}.
{"Offline messages", "Offline Nachrichten"}.
{"Last Activity", "Letzte Aktion"}.
{"Never", "Nie"}.
{"~s offline messages queue", "~s offline Nachrichtenschlange"}.
{"Time", "Uhrzeit"}.
{"From", "Von"}.
{"To", "Zu"}.
{"Packet", "Paket"}.
{"Offline messages:", "Offline Nachrichten"}.
{"Roster", "Liste"}.
{"Nickname", "Spitzname"}.
{"Subscription", "Subskription"}.
{"Pending", "schwebend"}.
{"Groups", "Gruppe"}.
{"Remove", "Entfernen"}.
{"Add JID", "JID hinzufügen"}.
{"User ", "Benutzer "}.
{"Roster of ", "Liste von "}.
{"Virtual Hosts", "Virtuelle Hosts"}.
{"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One", "ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One"}.
{"ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One", "ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One"}.
{"Shared Roster", "Gemeinsame Kontaktliste"}.
{"(raw)", "(roh)"}.
{"raw", "roh"}.
{"ejabberd virtual hosts", "ejabberd virtuelle Hosts"}.
{"Users last activity", "Letzte Aktivität des Benutzers"}.
{"Period: ", "Zeitraum"}.
{"Last month", "Letzter Monat"}.
{"Last year", "Letztes Jahr"}.
@@ -284,14 +226,9 @@
{"Show Integral Table", "Vollständige Tabelle anzeigen"}.
{"Host", "Host"}.
{"Online", "Online"}.
{"Authenticated users", "Angemeldete Benutzer"}.
{"Validate", "Validieren"}.
{"No data", "Keine Daten"}.
{"Modules Management", "Modulverwaltung"}.
{"Modules at ", "Module auf "}.
{"Not found", "Nicht gefunden"}.
{"Start", "Anfang"}.
{"Shared roster groups", "Gruppen der gemeinsamen Kontaktliste"}.
{"Name:", "Name"}.
{"Description:", "Beschreibung:"}.
{"Members:", "Mitglieder:"}.
@@ -307,9 +244,125 @@
{"Replaced by new connection", "Durch neue Verbindung ersetzt"}.
% mod_vcard_odbc.erl
{"Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin", "Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"Erlang Jabber Server\nCopyright (c) 2002-2006 Alexey Shchepin", "Erlang Jabber Server\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)", "Füllen Sie die Felder aus, um nach passenden Jabber benutzern zu suchen (beenden Sie ein Feld mit *, um auch nach Teilzeichenketten zu suchen)"}.
{"JID", "JID"}.
{"Email", "E-Mail"}.
{"Search Results for ", "Suchergebnisse für"}.
{"Jabber ID", "Jabber ID"}.
% mod_adhoc.erl
{"Commands", "Befehle"}.
{"Ping", "Ping"}.
{"Pong", "Pong"}.
% mod_announce.erl
{"Really delete message of the day?", "Wirklich die Nachricht des Tages löschen ?"}.
{"Subject", "Betreff"}.
{"Message body", "Nachrichtentext"}.
{"No body provided for announce message", "Keinen Text für die Ankündigung angegeben"}.
{"Announcements", "Ankündigungen"}.
{"Send announcement to all users", "Sende Ankündigung an alle Benutzer"}.
{"Send announcement to all online users", "Sende Ankündigung an alle online Benutzer"}.
{"Send announcement to all online users on all hosts", "Sende Ankündigung an alle online Benutzer auf allen Hosts"}.
{"Set message of the day and send to online users", "Setze Nachricht des Tages und sende sie an alle online Benutzer"}.
{"Update message of the day (don't send)", "Aktualisiere Nachricht des Tages (nicht senden)"}.
{"Delete message of the day", "Lösche Nachricht des Tages"}.
% mod_configure.erl
{"Database", "Datenbank"}.
{"Outgoing s2s Connections", "Ausgehende s2s Verbindungen"}.
{"Import Users From jabberd 1.4 Spool Files", "Importiere Benutzer von jabberd 1.4 Spool Dateien"}.
{"Database Tables Configuration at ", "Datenbank Tabellen Konfiguration bei "}.
% web/ejabberd_web_admin.erl
{"Administration", "Verwaltung"}.
{"(Raw)", "(Roh)"}.
{"Submitted", "Bestätigt"}.
{"Bad format", "Ungültiges Format"}.
{"Raw", "Roh"}.
{"Users Last Activity", "Letzte Benutzeraktivität"}.
{"Registered Users", "Registrierte Benutzer"}.
{"Offline Messages", "Offline Nachrichten"}.
{"Registered Users:", "Registrierte Benutzer:"}.
{"Authenticated Users:", "Überprüfte Benutzer:"}.
{"Online Users:", "Online Benutzer:"}.
{"Outgoing s2s Connections:", "Ausgehende s2s Verbindungen:"}.
{"Outgoing s2s Servers:", "Ausgehende s2s Server:"}.
{"Offline Messages:", "Offline Nachrichten:"}.
{"~s's Offline Messages Queue", "~s's Offline Nachrichten Warteschlange"}.
{"Add Jabber ID", "Jabber ID hinzufügen"}.
{"No Data", "Keine Daten"}.
{"Listened Ports", "Überwachte Ports"}.
{"RPC Call Error", "RPC Abruf-Fehler"}.
{"Database Tables at ", "Datenbank Tabellen bei "}.
{"Backup of ", "Sicherung von "}.
{"Remark that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.", "Beachten sie, daß diese Optionen nur die eingebaute Mnesia Datenbank sichert. Wenn sie das ODBC Modul verwenden, müssen sie die SQL Datenbank manuell sichern."}.
{"Store binary backup:", "Speichere binäres Sicherung:"}.
{"Restore binary backup immediately:", "Stelle binäre Sicherung sofort wiederher:"}.
{"Restore binary backup after next ejabberd restart (requires less memory):", "Stelle binäre Sicherung beim nächsten ejabberd Neustart wiederher (benötigt weniger Speicher):"}.
{"Store plain text backup:", "Speichere plaintext Sicherung:"}.
{"Restore plain text backup immediately:", "Stelle plaintext Sicherung sofort wiederher:"}.
{"Statistics of ~p", "Statistiken von ~p"}.
{"Uptime:", "Betriebszeit:"}.
{"CPU Time:", "CPU Zeit:"}.
{"Transactions Commited:", "Vorgänge durchgeführt:"}.
{"Transactions Aborted:", "Vorgänge abgebrochen:"}.
{"Transactions Restarted:", "Vorgänge neu gestartet:"}.
{"Transactions Logged:", "Vorgänge protokolliert:"}.
{"Update ", "Aktualisierung"}.
{"Update plan", "Aktualisierungsplan"}.
{"Updated modules", "Aktualisierte Module"}.
{"Update script", "Aktualisierungsscript"}.
{"Low level update script", "Low level Aktualisierungsscript"}.
{"Script check", "Script Überprüfung"}.
{"Not Found", "Nicht gefunden"}.
{"Shared Roster Groups", "Gruppen der gemeinsamen Kontaktliste"}.
% mod_muc/mod_muc_log.erl
{"Chatroom configuration modified", "Chatraum Konfiguration verändert"}.
{"joins the room", "kommt in den Raum"}.
{"leaves the room", "verlässt den Raum"}.
{"has been kicked", "wurde gekickt"}.
{"has been banned", "wurde gebannt"}.
{"is now known as", "ist nun bekannt als"}.
{"Monday", "Montag"}.
{"Tuesday", "Dienstag"}.
{"Wednesday", "Mittwoch"}.
{"Thursday", "Donnerstag"}.
{"Friday", "Freitag"}.
{"Saturday", "Samstag"}.
{"Sunday", "Sonntag"}.
{"January", "Jänner"}.
{"February", "Feber"}.
{"March", "März"}.
{"April", "April"}.
{"May", "Mai"}.
{"June", "Juni"}.
{"July", "Juli"}.
{"August", "August"}.
{"September", "September"}.
{"October", "Oktober"}.
{"November", "November"}.
{"December", "Dezember"}.
{"Room Configuration", "Raum Konfiguration"}.
% mod_muc/mod_muc_room.erl
{"This room is not anonymous", "Dieser Raum ist nicht anonym"}.
{"Make room persistent", "Raum persistent machen"}.
{"Make room public searchable", "Raum öffentlich durchsuchbar machen"}.
{"Make participants list public", "Teilnehmerliste öffentlich machen"}.
{"Make room password protected", "Raum passwortgeschützt machen"}.
{"Make room semianonymous", "Raum semi-anonym machen"}.
{"Make room members-only", "Raum members-only machen"}.
{"Make room moderated", "Raum modieriert machen"}.
{"Default users as participants", "Standartbenutzer als Teilnehmer"}.
{"Allow users to change subject", "Erlaube Benutzern Thema zu ändern"}.
{"Allow users to send private messages", "Erlaube Benutzern private Nachrichten zu senden"}.
{"Allow users to query other users", "Erlaube Benutzern andere Benutzer zu sehen(query)"}.
{"Allow users to send invites", "Erlaube Benutzern Einladungen zu senden"}.
{"Enable logging", "Log-Funktion aktivieren"}.
{"Description", "Beschreibung"}.
{"Number of occupants", "Anzahl der Teilnehmer"}.
% Local Variables:
% mode: erlang
+120 -68
View File
@@ -6,7 +6,6 @@
{"No resource provided", "No se ha proporcionado recurso"}.
% mod_configure.erl
{"DB Tables Configuration at ", "Configuración de tablas de la BD en "}.
{"Choose storage type of tables", "Selecciona tipo de almacenamiento de las tablas"}.
{"RAM copy", "Copia en RAM"}.
{"RAM and disc copy", "Copia en RAM y disco"}.
@@ -41,12 +40,15 @@
{"Action on user", "Acción en el usuario"}.
{"Edit Properties", "Editar propiedades"}.
{"Remove User", "Eliminar usuario"}.
{"Database", "Base de datos"}.
{"Outgoing s2s Connections", "Conexiones S2S salientes"}.
{"Import Users From jabberd 1.4 Spool Files", "Importar usuarios de ficheros spool de jabberd-1.4"}.
{"Database Tables Configuration at ", "Configuración de tablas de la base de datos en "}.
% mod_disco.erl
{"Configuration", "Configuración"}.
{"Online Users", "Usuarios conectados"}.
{"All Users", "Todos los usuarios"}.
{"Outgoing S2S connections", "Conexiones S2S de salida"}.
{"To ~s", "A ~s"}.
{"From ~s", "De ~s"}.
{"Running Nodes", "Nodos funcionando"}.
@@ -55,12 +57,9 @@
{"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"}.
@@ -71,12 +70,16 @@
{"Choose a username and password to register with this server", "Escoge un nombre de usuario y contraseña para registrarte en este servidor"}.
% mod_vcard.erl
{"Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin", "Servidor Jabber en Erlang\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"Erlang Jabber Server\nCopyright (c) 2002-2006 Alexey Shchepin", "Servidor Jabber en Erlang\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)", "Rellena el formulario para buscar usuarios Jabber. Añade * al final de un campo para buscar subcadenas."}.
{"ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo vCard para ejabberd\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"ejabberd vCard module\nCopyright (c) 2003-2006 Alexey Shchepin", "Módulo vCard para ejabberd\nCopyright (c) 2002-2006 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 "}.
{"Results of search in ", "Resultados de la búsqueda en "}.
% mod_vcard_odbc.erl
{"Email", "correo"}.
{"Search Results for ", "Buscar resultados por "}.
{"Jabber ID", "Jabber ID"}.
{"User", "Usuario"}.
{"Full Name", "Nombre completo"}.
@@ -87,12 +90,11 @@
{"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-2005 Alexey Shchepin", "Módulo Pub/Sub para ejabberd\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"ejabberd pub/sub module\nCopyright (c) 2003-2006 Alexey Shchepin", "Módulo Pub/Sub para ejabberd\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"Node Creator", "Creador del nodo"}.
{[], " "}.
{"Deliver payloads with event notifications", "Enviar payloads junto con las notificaciones de eventos"}.
@@ -116,30 +118,30 @@
{"Only service administrators are allowed to send service messages", "Solo los administradores del servicio tienen permiso para enviar mensajes de servicio"}.
{"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"}.
{"You must fill in field \"Nickname\" in the form", "Debes rellenar el campo \"Apodo\" en el formulario"}.
{"Specified nickname is already registered", "El apodo especificado ya está registrado, tendrás que buscar otro"}.
{"Room creation is denied by service policy", "Se te ha denegado crear la sala por política del servicio"}.
{"ejabberd MUC module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo de MUC para ejabbed\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"ejabberd MUC module\nCopyright (c) 2003-2006 Alexey Shchepin", "Módulo de MUC para ejabbed\nCopyright (c) 2002-2006 Alexey Shchepin"}.
% 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"}.
{"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", "Sala públicamente visible"}.
{"Make participants list public", "La lista de participantes es pública"}.
{"Make room persistent", "Sala permanente"}.
{"Make room moderated", "Sala moderada"}.
{"Default users as participants", "Los usuarios on participantes por defecto"}.
{"Make room members-only", "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"}.
{"This room is not anonymous", "Sala no anónima"}.
{"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"}.
@@ -165,7 +167,37 @@
{"Invalid role: ~s", "Rol no válido: ~s"}.
{"Owner privileges required", "Se requieren privilegios de propietario de la sala"}.
{"private, ", "privado"}.
{"Make room semianonymous", "Hacer la sala semianónima"}.
{"Description", "Descripción"}.
{"Number of occupants", "Número de ocupantes"}.
% mod_muc/mod_muc_log.erl
{"Chatroom configuration modified", "Configuración de la sala modificada"}.
{"joins the room", "entra en la sala"}.
{"leaves the room", "sale de la sala"}.
{"has been kicked", "ha sido expulsado"}.
{"has been banned", "ha sido bloqueado"}.
{"is now known as", "se cambia el nombre a"}.
{"Monday", "lunes"}.
{"Tuesday", "martes"}.
{"Wednesday", "miércoles"}.
{"Thursday", "jueves"}.
{"Friday", "viernes"}.
{"Saturday", "sábado"}.
{"Sunday", "domingo"}.
{"January", "enero"}.
{"February", "febrero"}.
{"March", "marzo"}.
{"April", "abril"}.
{"May", "mayo"}.
{"June", "junio"}.
{"July", "julio"}.
{"August", "agosto"}.
{"September", "septiembre"}.
{"October", "octubre"}.
{"November", "noviembre"}.
{"December", "diciembre"}.
{"Room Configuration", "Configuración de la sala"}.
% mod_irc/mod_irc.erl
{"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"}.
@@ -173,112 +205,115 @@
{"IRC Username", "Nombre de usuario en IRC"}.
{"If you want to specify different encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\"}'. By default this service use \"~s\" encoding.", "Si quieres especificar codificaciones de carácteres distintos para cada servidor IRC rellena esta lista con valores en el formato '{\"servidor irc\", \"codificación\"}'. Este servicio usa por defecto la codificación \"~s\"."}.
{"Encodings", "Codificaciones"}.
{"ejabberd IRC module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo de IRC para ejabberd\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"ejabberd IRC module\nCopyright (c) 2003-2006 Alexey Shchepin", "Módulo de IRC para ejabberd\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"Example: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}].", "Ejemplo: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}]."}.
% web/ejabberd_web_admin.erl
{"ejabberd administration", "Administración de ejabberd"}.
{"Users", "Usuarios"}.
{"Nodes", "Nodos"}.
{"Statistics", "Estadísticas"}.
{"(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"}.
{"Online users", "Usuarios conectados"}.
{"Outgoing S2S servers", "Servidores conectados"}.
{"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"}.
{"Backup Management", "Gestión de copia de seguridad"}.
{"OK", "Aceptar"}.
{"Restore a backup from a file", "Recuperar una copia de seguridad de un fichero"}.
{"Install a database fallback from a file", "Instalar una recuperación de BD desde un fichero"}.
{"Dump a database in a text file", "Exportar una Base de Datos a un fichero de texto"}.
{"Restore a database from a text file", "Restaurar una Base de Datos desde un fichero de texto"}.
{"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", "Añadir usuario"}.
{"Offline messages", "Mensajes diferidos"}.
{"Last Activity", "Última actividad"}.
{"Never", "Nunca"}.
{"~s offline messages queue", "~s cola de mensajes diferidos"}.
{"Time", "Fecha"}.
{"From", "De"}.
{"To", "Para"}.
{"Packet", "Paquete"}.
{"Offline messages:", "Mensajes diferidos:"}.
{"Roster", "Lista de contactos"}.
{"Nickname", "Apodo"}.
{"Subscription", "Subscripción"}.
{"Pending", "Pendiente"}.
{"Groups", "Grupos"}.
{"Remove", "Borrar"}.
{"Add JID", "Añadir JID"}.
{"User ", "Usuario "}.
{"Roster of ", "Lista de contactos de "}.
{"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One", "ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One"}.
{"ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One", "ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One"}.
{"Shared Roster", "Grupos Compartidos"}.
{"Online", "Conectado"}.
{"JID", "JID"}.
{"Validate", "Validar"}.
{"Not found", "No encontrado"}.
{"Shared roster groups", "Grupos Compartidos"}.
{"Name:", "Nombre:"}.
{"Description:", "Descripción:"}.
{"Members:", "Miembros:"}.
{"Displayed Groups:", "Mostrar grupos:"}.
{"Group ", "Grupo "}.
{"Users last activity", "Última actividad de los usuarios"}.
{"Period: ", "Periodo: "}.
{"Last month", "Último mes"}.
{"Last year", "Último año"}.
{"All activity", "Toda la actividad"}.
{"Show Ordinary Table", "Mostrar Tabla Ordinaria"}.
{"Show Integral Table", "Mostrar Tabla Integral"}.
{"Authenticated users", "Usuarios autenticados"}.
{"No data", "No hay datos"}.
{"Modules Management", "Gestión de módulos"}.
{"Modules at ", "Módulos en "}.
{"Start", "Iniciar"}.
{"Virtual Hosts", "Hosts Virtuales"}.
{"ejabberd virtual hosts", "Hosts virtuales de ejabberd"}.
{"Host", "Host"}.
{"ejabberd Web Interface", "Interfaz Web de ejabberd"}.
{"Restore plain text backup immediately:", "Restaurar copias de seguridad de texto plano inmediatamente:"}.
{"Statistics of ~p", "Estadísticas de ~p"}.
{"Uptime:", "Tiempo desde el inicio:"}.
{"CPU Time:", "Tiempo consumido de CPU:"}.
{"Transactions Commited:", "Transacciones finalizadas:"}.
{"Transactions Aborted:", "Transacciones abortadas:"}.
{"Transactions Restarted:", "Transacciones reiniciadas:"}.
{"Transactions Logged:", "Transacciones registradas:"}.
{"Update ", "Actualizar"}.
{"Update plan", "Plan de actualización"}.
{"Updated modules", "Módulos actualizados"}.
{"Update script", "Script de actualización"}.
{"Low level update script", "Script de actualización a bajo nivel"}.
{"Script check", "Comprobación de script"}.
{"Not Found", "No encontrado"}.
{"Shared Roster Groups", "Grupos Compartidos"}.
{"Administration", "Administración"}.
{"(Raw)", "(Crudo)"}.
{"Submitted", "Enviado"}.
{"Bad format", "Mal formato"}.
{"Raw", "Crudo"}.
{"Users Last Activity", "Última actividad de los usuarios"}.
{"Registered Users", "Usuarios registrados"}.
{"Offline Messages", "Mensajes diferidos"}.
{"Registered Users:", "Usuarios registrados:"}.
{"Authenticated Users:", "Usuarios autenticados:"}.
{"Online Users:", "Usuarios conectados:"}.
{"Outgoing s2s Connections:", "Conexiones S2S salientes:"}.
{"Outgoing s2s Servers:", "Servidores S2S salientes:"}.
{"Offline Messages:", "Mensajes diferidos:"}.
{"~s's Offline Messages Queue", "Cola de mensajes diferidos de ~s"}.
{"Add Jabber ID", "Añadir Jabber ID"}.
{"No Data", "Sin datos"}.
{"Listened Ports", "Puertos de escucha"}.
{"RPC Call Error", "Error en la llamada RPC"}.
{"Database Tables at ", "Tablas de la base de datos en "}.
{"Backup of ", "Copia de seguridad de "}.
{"Remark that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.", "Ten en cuenta que estas opciones solo harán copia de seguridad de la base de datos Mnesia embebida. Si estás usando ODBC tendrás que hacer también copia de seguridad de tu base de datos SQL."}.
{"Store binary backup:", "Guardar copia de seguridad binaria:"}.
{"Restore binary backup immediately:", "Restaurar inmediatamente copia de seguridad binaria:"}.
{"Restore binary backup after next ejabberd restart (requires less memory):", "Restaurar copia de seguridad binaria en el siguiente reinicio de ejabberd (requiere menos memoria que si instantánea):"}.
{"Store plain text backup:", "Guardar copia de seguridad en texto plano:"}.
% ejabberd_c2s.erl
{"Use of STARTTLS required", "Es obligatorio usar STARTTLS"}.
@@ -286,9 +321,26 @@
% mod_vcard_ldap.erl
{"Given Name", "Nombre dado"}.
{"JID", "JID"}.
{"Fill in fields to search for any matching Jabber User", "Rellena campos para buscar usuarios Jabber que concuerden"}.
% mod_adhoc.erl
{"Commands", "Comandos"}.
{"Ping", "Ping"}.
{"Pong", "Pong"}.
% mod_announce.erl
{"Really delete message of the day?", "¿Está seguro de quere borrar el mensaje del dia?"}.
{"Subject", "Asunto"}.
{"Message body", "Cuerpo del mensaje"}.
{"No body provided for announce message", "No se ha proporcionado cuerpo de mensaje para el anuncio"}.
{"Announcements", "Anuncios"}.
{"Send announcement to all users", "Enviar anuncio a todos los usuarios"}.
{"Send announcement to all online users", "Enviar anuncio a todos los usuarios conectados"}.
{"Send announcement to all online users on all hosts", "Enviar anuncio a todos los usuarios conectados en todos los dominios"}.
{"Set message of the day and send to online users", "Poner mensaje del dia y enviar a todos los usuarios conectados"}.
{"Update message of the day (don't send)", "Actualizar mensaje del dia, pero no enviarlo"}.
{"Delete message of the day", "Borrar mensaje del dia"}.
% Local Variables:
% mode: erlang
% End:
+118 -73
View File
@@ -4,7 +4,6 @@
{"No resource provided", "Aucune ressource fournie"}.
% mod_configure.erl
{"DB Tables Configuration at ", "Configuration des tables de BdD sur "}.
{"Choose storage type of tables", "Choisissez un type de stockage pour les tables"}.
{"RAM copy", "Copie en mémoire vive (RAM)"}.
{"RAM and disc copy", "Copie en mémoire vive (RAM) et sur disque"}.
@@ -39,6 +38,10 @@
{"Action on user", "Action sur l'utilisateur"}.
{"Edit Properties", "Modifier les propriétés"}.
{"Remove User", "Supprimer l'utilisateur"}.
{"Database", "Base de données"}.
{"Outgoing s2s Connections", "Connexions s2s sortantes"}.
{"Import Users From jabberd 1.4 Spool Files", "Importer des utilisateurs depuis un fichier spool Jabberd 1.4"}.
{"Database Tables Configuration at ", "Configuration des tables de base de données sur "}.
% src/ejabberd_c2s.erl
{"Use of STARTTLS required", "L'utilisation de STARTTLS est impérative"}.
@@ -48,7 +51,6 @@
{"Configuration", "Configuration"}.
{"Online Users", "Utilisateurs en ligne"}.
{"All Users", "Tous les utilisateurs"}.
{"Outgoing S2S connections", "Connexion S2S sortantes"}.
{"To ~s", "A ~s"}.
{"From ~s", "De ~s"}.
{"Running Nodes", "Noeuds actifs"}.
@@ -57,12 +59,10 @@
{"Access Control Lists", "Droits (ACL)"}.
{"Access Rules", "Règles d'accès"}.
{"Remove Users", "Supprimer des utilisateurs"}.
{"DB", "BdD"}.
{"Modules", "Modules"}.
{"Start Modules", "Modules de démarrage"}.
{"Stop Modules", "Modules d'arrêt"}.
{"Backup Management", "Gestion des sauvegardes"}.
{"Import users from jabberd1.4 spool files", "Importer des utilisateurs depuis un fichier spool jabberd1.4"}.
{"Backup", "Sauvegarde"}.
{"Restore", "Restauration"}.
{"Dump to Text File", "Sauvegarder dans un fichier texte"}.
@@ -73,10 +73,12 @@
{"Choose a username and password to register with this server", "Choisissez un nom d'utilisateur et un mot de passe pour s'enregistrer sur ce serveur"}.
% src/mod_vcard_ldap.erl
{"Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin", "Serveur Jabber Erlang\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"Erlang Jabber Server\nCopyright (c) 2002-2006 Alexey Shchepin", "Serveur Jabber Erlang\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"Given Name", "Prénom"}.
{"ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin", "Module vCard ejabberd\nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"JID", "JID"}.
{"ejabberd vCard module\nCopyright (c) 2003-2006 Alexey Shchepin", "Module vCard ejabberd\nCopyright (c) 2003-2006 Alexey Shchepin"}.
{"Email", "Email"}.
{"Search Results for ", "Résultats de recherche pour "}.
{"Jabber ID", "Jabber ID"}.
% mod_vcard.erl
{"You need an x:data capable client to search",
@@ -86,7 +88,6 @@
{"Fill in fields to search for any matching Jabber User",
"Remplissez les champs pour rechercher un utilisateur Jabber"}.
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)", "Remplissez le formulaire pour recherche un utilisateur Jabber (Ajouter * à la fin du champ pour chercher n'importe quelle fin de chaîne"}.
{"Results of search in ", "Résultat de la recherche des utilisateurs "}.
{"User", "Utilisateur"}.
{"Full Name", "Nom complet"}.
@@ -97,12 +98,29 @@
{"Birthday", "Date d'anniversaire"}.
{"Country", "Pays"}.
{"City", "Ville"}.
{"email", "email"}.
{"Organization Name", "Nom de l'organisation"}.
{"Organization Unit", "Unité de l'organisation"}.
% mod_adhoc.erl
{"Commands", "Commandes"}.
{"Ping", "Ping"}.
{"Pong", "Pong"}.
% mod_announce.erl
{"Really delete message of the day?", "Confirmer la suppression du message du jour ?"}.
{"Subject", "Sujet"}.
{"Message body", "Corps du message"}.
{"No body provided for announce message", "Pas de corps de message pour l'annonce"}.
{"Announcements", "Annonces"}.
{"Send announcement to all users", "Envoyer l'annonce à tous les utilisateurs"}.
{"Send announcement to all online users", "Envoyer l'annonce à tous les utilisateurs en ligne"}.
{"Send announcement to all online users on all hosts", "Envoyer l'annonce à tous les utilisateurs en ligne sur tous les serveurs"}.
{"Set message of the day and send to online users", "Définir le message du jour et l'envoyer aux utilisateurs en ligne"}.
{"Update message of the day (don't send)", "Mise à jour du message du jour (pas d'envoi)"}.
{"Delete message of the day", "Supprimer le message du jour"}.
% mod_pubsub/mod_pubsub.erl
{"ejabberd pub/sub module\nCopyright (c) 2003-2005 Alexey Shchepin", "Module pub/sub ejabberd \nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"ejabberd pub/sub module\nCopyright (c) 2003-2006 Alexey Shchepin", "Module pub/sub ejabberd \nCopyright (c) 2003-2006 Alexey Shchepin"}.
{[], ""}.
{"Node Creator", "Créateur du noeud"}.
{"Deliver payloads with event notifications", "Inclure le contenu du message avec la notification"}.
@@ -119,43 +137,52 @@
{"Only deliver notifications to available users", "Envoyer les notifications uniquement aux utilisateurs disponibles"}.
{"Specify the current subscription approver", "Définir l'utilisateur qui valide les abonnements"}.
% mod_muc/mod_muc_log.erl
{"Chatroom configuration modified", "Configuration du salon modifiée"}.
{"joins the room", "rejoint le salon"}.
{"leaves the room", "quitte le salon"}.
{"has been kicked", "a été expulsé"}.
{"has been banned", "a été banni"}.
{"is now known as", "est maintenant connu comme"}.
{"Monday", "Lundi"}.
{"Tuesday", "Mardi"}.
{"Wednesday", "Mercredi"}.
{"Thursday", "Jeudi"}.
{"Friday", "Vendredi"}.
{"Saturday", "Samedi"}.
{"Sunday", "Dimanche"}.
{"January", "Janvier"}.
{"February", "Février"}.
{"March", "Mars"}.
{"April", "Avril"}.
{"May", "Mai"}.
{"June", "Juin"}.
{"July", "Juillet"}.
{"August", "Août"}.
{"September", "Septembre"}.
{"October", "Octobre"}.
{"November", "Novembre"}.
{"December", "Décembre"}.
{"Room Configuration", "Configuration du salon"}.
% mod_muc/mod_muc.erl
{"You need an x:data capable client to register nickname", "Vous avez besoin d'un client supportant x:data pour enregistrer un pseudo"}.
{"Nickname Registration at ", "Enregistrement d'un pseudo sur "}.
{"Enter nickname you want to register", "Entrez le pseudo que vous souhaitez enregistrer"}.
{"ejabberd MUC module\nCopyright (c) 2003-2005 Alexey Shchepin", "Module MUC ejabberd\nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"ejabberd MUC module\nCopyright (c) 2003-2006 Alexey Shchepin", "Module MUC ejabberd\nCopyright (c) 2003-2006 Alexey Shchepin"}.
{"Only service administrators are allowed to send service messages", "Seuls les administrateurs du service sont autoriser à envoyer des messages de service"}.
{"Room creation is denied by service policy", "La création de salons est interdite par le service"}.
{"Conference room does not exist", "La salle de conférence n'existe pas"}.
{"Access denied by service policy", "L'accès au service est refusé"}.
{"You must fill in field \"nick\" in the form", "Vous devez préciser le champ \"pseudo\" dans le formulaire"}.
{"Specified nickname is already registered", "Le pseudo demandé est déjà enregistré"}.
{"You must fill in field \"Nickname\" in the form", "Vous devez préciser le champ \"pseudo\" dans le formulaire"}.
% mod_muc/mod_muc_room.erl
{" has set the subject to: ", " a changé le sujet pour: "}.
{"You need an x:data capable client to configure room", "Vous avez besoin d'un client supportant x:data pour configurer le salon"}.
{"Configuration for ", "Configuration pour "}.
{"Room title", "Titre du salon"}.
{"Allow users to change subject?", "Autoriser les utilisateurs à changer le sujet ?"}.
{"Allow users to query other users?",
"Autoriser les utilisateurs à faire une requête à d'autres utilisateurs ?"}.
{"Allow users to send private messages?",
"Autoriser les utilisateurs à envoyer des messages privés ?"}.
{"Make room public searchable?", "Créer un salon public avec recherche ?"}.
{"Make participants list public?", " Créer une liste publique de participants ?"}.
{"Make room persistent?", "Créer un salon persistant ?"}.
{"Make room moderated?", "Créer un salon modéré ?"}.
{"Default users as members?",
"Les utilisateurs sont membres par défaut ?"}.
{"Make room members only?",
"Créer un salon réservé aux membres uniquement ?"}.
{"Allow users to send invites?",
"Autoriser des utilisateurs à envoyer des invitations ?"}.
{"Make room password protected?", "Créer un salon protégé par mot de passe ?"}.
{"Password", "Mot de passe"}.
{"Make room anonymous?", "Créer un salon anonyme ?"}.
{"Enable logging?", "Activer la journalisation ?"}.
{"Only moderators and participants are allowed to change subject in this room", "Seuls les modérateurs et les participants peuvent changer le sujet dans ce salon"}.
{"Only moderators are allowed to change subject in this room", "Seuls les modérateurs peuvent changer le sujet dans ce salon"}.
{"Visitors are not allowed to send messages to all occupants", "Les visiteurs ne sont pas autorisés à envoyer des messages à tout les occupants"}.
@@ -181,9 +208,25 @@
{"Invalid role: ~s", "Role invalide: ~s"}.
{"Owner privileges required", "Les droits de propriétaire sont nécessaires"}.
{"private, ", "privé"}.
{"This room is not anonymous", "Ce salon n'est pas anonyme"}.
{"Make room persistent", "Rendre le salon persistant"}.
{"Make room public searchable", "Rendre le salon public"}.
{"Make participants list public", "Rendre la liste des participants publique"}.
{"Make room password protected", "Protéger le salon par mot de passe"}.
{"Make room semianonymous", "Rendre le salon semi-anonyme"}.
{"Make room members-only", "Réserver le salon aux membres uniquement"}.
{"Make room moderated", "Rendre le salon modéré"}.
{"Default users as participants", "Les utilisateurs sont par défaut participant"}.
{"Allow users to change subject", "Autoriser les utilisateurs à changer le sujet"}.
{"Allow users to send private messages", "Autoriser les utilisateurs à envoyer des messages privés"}.
{"Allow users to query other users", "Permettre aux utilisateurs d'envoyer des requêtes aux autres utilisateurs"}.
{"Allow users to send invites", "Permettre aux utilisateurs d'envoyer des invitations"}.
{"Enable logging", "Activer l'archivage"}.
{"Description", "Description"}.
{"Number of occupants", "Nombre d'occupants"}.
% mod_irc/mod_irc.erl
{"ejabberd IRC module\nCopyright (c) 2003-2005 Alexey Shchepin", "Module IRC ejabberd\nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"ejabberd IRC module\nCopyright (c) 2003-2006 Alexey Shchepin", "Module IRC ejabberd\nCopyright (c) 2003-2006 Alexey Shchepin"}.
{"You need an x:data capable client to configure mod_irc settings", "Vous avez besoin d'un client supportant x:data pour configurer le module IRC"}.
{"Registration in mod_irc for ", "Enregistrement du mod_irc pour "}.
{"Enter username and encodings you wish to use for connecting to IRC servers", "Entrez le nom d'utilisateur et les encodages que vous souhaitez utiliser pour vous connecter aux serveurs IRC"}.
@@ -193,108 +236,110 @@
{"Encodings", "Encodages"}.
% web/ejabberd_web_admin.erl
{"ejabberd administration", "Administration d'ejabberd"}.
{"Administration", "Administration"}.
{"Users", "Utilisateurs"}.
{"Nodes", "Noeuds"}.
{"Shared Roster", "Liste de contacts partagée"}.
{"Statistics", "Statistiques"}.
{"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One", "ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One"}.
{"(raw)", "(brut)"}.
{"submitted", "Soumis"}.
{"bad format", "Mauvais format"}.
{"raw", "brut"}.
{"ejabberd access control lists configuration", "Configuration des droits (ACL) ejabberd"}.
{"ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One", "ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One"}.
{"(Raw)", "(Brut)"}.
{"Submitted", "Soumis"}.
{"Bad format", "Mauvais format"}.
{"Raw", "Brut"}.
{"Delete Selected", "Suppression des éléments sélectionnés"}.
{"Submit", "Soumettre"}.
{"ejabberd access rules configuration", "Configuration des règles d'accès ejabberd"}.
{"~s access rule configuration", "Configuration des règles d'accès ~s"}.
{"ejabberd users", "Utilisateurs ejabberd"}.
{"ejabberd stats", "Statistiques ejabberd"}.
{"Node not found", "Noeud non trouvé"}.
{"Add New", "Ajouter"}.
{"Registered users", "Utilisateurs enregistrés"}.
{"Online users", "Utilisateurs connectés"}.
{"Outgoing S2S servers", "Connexions S2S sortantes"}.
{"Registered Users", "Utilisateurs enregistrés"}.
{"Registered Users:", "Utilisateurs enregistrés:"}.
{"Change Password", "Modifier le mot de passe"}.
{"Connected Resources:", "Ressources connectées:"}.
{"Password:", "Mot de passe:"}.
{"None", "Aucun"}.
{"Node ", "Noeud "}.
{"DB Management", "Gestion BdD"}.
{"Listened Ports Management", "Gestion des ports ouverts"}.
{"Listened Ports", "Ports ouverts"}.
{"Restart", "Redémarrer"}.
{"Stop", "Arrêter"}.
{"RPC call error", "Erreur d'appel RPC"}.
{"DB Tables at ", "Table de BdD sur "}.
{"RPC Call Error", "Erreur d'appel RPC"}.
{"Name", "Nom"}.
{"Storage Type", "Type de stockage"}.
{"Size", "Taille"}.
{"Memory", "Mémoire"}.
{"Backup Management at ", "Gestion des sauvegardes sur "}.
{"Store a backup in a file", "Sauvegarder les données dans un fichier"}.
{"OK", "OK"}.
{"Restore a backup from a file", "Recharger une sauvegarde depuis un fichier"}.
{"Install a database fallback from a file", "Installer un point de contrôle de base de données depuis un fichier"}.
{"Dump a database in a text file", "Sauvegarder la base de données dans un fichier texte"}.
{"Restore a database from a text file", "Restaurer la base de données depuis un fichier texte"}.
{"Listened Ports at ", "Ports ouverts sur "}.
{"~p statistics", "Statistiques ~p"}.
{"Uptime", "Temps depuis le démarrage"}.
{"CPU Time", "Temps CPU"}.
{"Transactions commited", "Transactions commitées"}.
{"Transactions aborted", "Transactions annulées"}.
{"Transactions restarted", "Transactions redémarrées"}.
{"Transactions logged", "Transactions journalisées"}.
{"Uptime:", "Temps depuis le démarrage:"}.
{"CPU Time:", "Temps CPU:"}.
{"Transactions Commited:", "Transactions commitées:"}.
{"Transactions Aborted:", "Transactions annulées:"}.
{"Transactions Restarted:", "Transactions redémarrées:"}.
{"Transactions Logged:", "Transactions journalisées:"}.
{"Port", "Port"}.
{"Module", "Module"}.
{"Options", "Options"}.
{"Update", "Mettre à jour"}.
{"Delete", "Supprimer"}.
{"Add User", "Ajouter un utilisateur"}.
{"Offline messages", "Messages en attente"}.
{"Last Activity", "Dernière activité"}.
{"Offline Messages", "Messages en attente"}.
{"Users Last Activity", "Dernière activité des utilisateurs"}.
{"Never", "Jamais"}.
{"~s offline messages queue", "~s messages en file d'attente"}.
{"~s's Offline Messages Queue", "~s messages en file d'attente"}.
{"Time", "Heure"}.
{"From", "De"}.
{"To", "A"}.
{"Packet", "Paquet"}.
{"Offline messages:", "Messages en attente:"}.
{"Offline Messages:", "Messages en attente:"}.
{"Roster", "Liste de contacts"}.
{"Nickname", "Pseudo"}.
{"Subscription", "Abonnement"}.
{"Pending", "En suspend"}.
{"Groups", "Groupes"}.
{"Remove", "Enlever"}.
{"Add JID", "Ajouter un JID"}.
{"Add Jabber ID", "Ajouter un Jabber ID"}.
{"User ", "Utilisateur"}.
{"Roster of ", "Liste de contact de "}.
{"Online", "En ligne"}.
{"Validate", "Valider"}.
{"Not found", "Non trouvé"}.
{"Shared roster groups", "Groupes de la liste de contacts partagée"}.
{"Name:", "Nom:"}.
{"Description:", "Description:"}.
{"Members:", "Membres:"}.
{"Displayed Groups:", "Groupes affichés:"}.
{"Group ", "Groupe "}.
{"Users last activity", "Dernière activité des utilisateurs"}.
{"Period: ", "Période:"}.
{"Last month", "Dernier mois"}.
{"Last year", "Dernière année"}.
{"All activity", "Toute activité"}.
{"Show Ordinary Table", "Montrer la table ordinaire"}.
{"Show Integral Table", "Montrer la table intégralement"}.
{"Modules Management", "Gestion des modules"}.
{"Modules at ", "Modules sur "}.
{"Start", "Démarrer"}.
{"Virtual Hosts", "Serveurs virtuels"}.
{"ejabberd virtual hosts", "Serveurs virtuels d'ejabberd"}.
{"Host", "Serveur"}.
{"Authenticated users", "Utilisateurs authentifiés"}.
{"No data", "Aucune information disponible"}.
{"Authenticated Users:", "Utilisateurs authentifiés:"}.
{"No Data", "Aucune information disponible"}.
{"ejabberd Web Interface", "Ejabberd - Interface Web"}.
{"Online Users:", "Utilisateurs connectés:"}.
{"Outgoing s2s Connections:", "Connexions s2s sortantes:"}.
{"Outgoing s2s Servers:", "Serveurs s2s sortants"}.
{"Database Tables at ", "Tables de base de données sur "}.
{"Backup of ", "Sauvegarde de "}.
{"Remark that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.", "Ces options sauvegardent uniquement la base de données interne Mnesia. Si vous utilisez le module ODBC vous devez sauvegarde votre base SQL séparément."}.
{"Store binary backup:", "Sauvegarde binaire:"}.
{"Restore binary backup immediately:", "Restauration immédiate d'une sauvegarde binaire:"}.
{"Restore binary backup after next ejabberd restart (requires less memory):", "Restauration de la sauvegarde binaire après redémarrage (nécessite moins de mémoire):"}.
{"Store plain text backup:", "Sauvegarde texte:"}.
{"Restore plain text backup immediately:", "Restauration immédiate d'une sauvegarde texte:"}.
{"Statistics of ~p", "Statistiques de ~p"}.
{"Update ", "Mise à jour "}.
{"Update plan", "Plan de mise à jour"}.
{"Updated modules", "Modules mis à jours"}.
{"Update script", "Script de mise à jour"}.
{"Low level update script", "Script de mise à jour de bas-niveau"}.
{"Script check", "Validation du script"}.
{"Not Found", "Non trouvé"}.
{"Shared Roster Groups", "Groupes de liste de contacts partagée"}.
{"Last Activity", "Dernière Activité"}.
% Local Variables:
% mode: erlang
+281 -265
View File
@@ -1,37 +1,82 @@
% $Id$
% jlib.hrl
{"No resource provided", "Geen bron opgegeven"}.
% mod_vcard_ldap.erl
{"Erlang Jabber Server\nCopyright (c) 2002-2006 Alexey Shchepin", "Erlang Jabber Server\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"You need an x:data capable client to search", "U hebt een client nodig die x:data ondersteunt om te 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 Jabber-gebruikers op deze server"}.
{"User", "Gebruiker"}.
{"Full Name", "Volledige naam"}.
{"Given 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"}.
{"ejabberd vCard module\nCopyright (c) 2003-2006 Alexey Shchepin", "ejabberd's vCard-module\nCopyright (c) 2003-2006 Alexey Shchepin"}.
{"Search Results for ", "Zoekresultaten voor "}.
{"Jabber ID", "Jabber ID"}.
% mod_vcard.erl
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)", "Gebruik de velden om te zoeken (Voeg achteraan het teken * toe om te zoeken naar alles wat met het eerste deel begint.)."}.
{"Name", "Naam"}.
% Probably needs to be "Given Name"
% mod_configure.erl
{"DB Tables Configuration at ", "Databasetabellenconfiguratie op "}.
{"Configuration", "Instellingen"}.
{"Database", "Database"}.
{"Start Modules", "Modules starten"}.
{"Stop Modules", "Modules stoppen"}.
{"Backup", "Backup"}.
{"Restore", "Binaire backup direct herstellen"}.
{"Dump to Text File", "Backup naar een tekstbestand schrijven"}.
{"Import File", "Bestand importeren"}.
{"Import Directory", "Directory importeren"}.
{"Host Name", "Hostnaam"}.
{"Access Control Lists", "Access control lists"}.
{"Access Rules", "Access rules"}.
{"Online Users", "Online gebruikers"}.
{"All Users", "Alle gebruikers"}.
{"Outgoing s2s Connections", "Uitgaande s2s-verbindingen"}.
{"Running Nodes", "Draaiende nodes"}.
{"Stopped Nodes", "Gestopte nodes"}.
{"Modules", "Modules"}.
{"Backup Management", "Backup"}.
{"Import Users From jabberd 1.4 Spool Files", "Importeer gebruikers via spool-bestanden van jabberd 1.4"}.
{"To ~s", "Naar ~s"}.
{"From ~s", "Van ~s"}.
{"Database Tables Configuration at ", "Instellingen van databasetabellen 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"}.
{"RAM copy", "RAM"}.
{"RAM and disc copy", "RAM en harde schijf"}.
{"Disc only copy", "Harde schijf"}.
{"Remote copy", "Op andere nodes in de cluster"}.
{"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]}"}.
{"Enter list of {Module, [Options]}", "Voer lijst met op te starten modules als volgt in: {Module, [Opties]}"}.
{"List of modules to start", "Lijst met op te starten modules"}.
{"Backup to File at ", "Backup naar bestand maken op "}.
{"Backup to File at ", "Binaire backup 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"}.
{"Restore Backup from File at ", "Binaire backup direct herstellen op "}.
{"Dump Backup to Text File at ", "Backup naar een tekstbestand schrijven op "}.
{"Enter path to text file", "Voer pad naar backupbestand 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"}.
{"Enter path to jabberd1.4 spool file", "Voer pad naar jabberd 1.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"}.
{"Enter path to jabberd1.4 spool dir", "Voer pad naar jabberd 1.4-spool-directory in"}.
{"Path to Dir", "Pad naar directory"}.
{"Hostname Configuration", "Hostnaam configuratie"}.
{"Hostname Configuration", "Instellingen van hostnaam"}.
{"Choose host name", "Hostnaam kiezen"}.
{"Host name", "Hostnaam"}.
{"Access Control List Configuration", "Access Control List Configuratie"}.
{"Access Control List Configuration", "Instellingen van access control lists"}.
{"Access control lists", "Access control lists"}.
{"Access Configuration", "Toegangsconfiguratie"}.
{"Access Configuration", "Toegangsinstellingen"}.
{"Access rules", "Access rules"}.
{"Remove Users", "Gebruikers verwijderen"}.
{"Choose users to remove", "Selecteer de gebruikers die u wilt verwijderen"}.
@@ -39,279 +84,250 @@
{"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 jabberd 1.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"}.
{"Password", "Wachtwoord"}.
% 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"}.
{"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
{"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 Jabber-gebruikers op deze server"}.
{"Results of search in ", "Zoekresultaten van "}.
{"Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin", "Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)", "Vul het formulier in om elke overeenkomende Jabber-gebruiker te zoeken (Voeg achteraan het teken * toe om te zoeken naar alles wat met het eerste deel begint.)."}.
{"ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin", "ejabberd's vCard module\nCopyright (c) 2003-2005 Alexey Shchepin"}.
% ejabberd_c2s.erl
{"Use of STARTTLS required", "Gebruik van STARTTLS is vereist"}.
{"No resource provided", "Geen bron opgegeven"}.
{"Replaced by new connection", "Vervangen door een nieuwe verbinding"}.
{"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"}.
{"JID", "JID"}.
% mod_muc/mod_muc_log.erl
{"Chatroom configuration modified", "De instellingen van de chatruimte werden veranderd"}.
{"joins the room", "betrad de chatruimte"}.
{"leaves the room", "verliet de chatruimte"}.
{"has been kicked", "werd gekicked"}.
{"has been banned", "werd verbannen"}.
{"is now known as", "heet nu"}.
{" has set the subject to: ", " veranderde het onderwerp in: "}.
{"Monday", "maandag"}.
{"Tuesday", "dinsdag"}.
{"Wednesday", "woensdag"}.
{"Thursday", "donderdag"}.
{"Friday", "vrijdag"}.
{"Saturday", "zaterdag"}.
{"Sunday", "zondag"}.
{"January", "januari"}.
{"February", "februari"}.
{"March", "maart"}.
{"April", "april"}.
{"May", "mei"}.
{"June", "juni"}.
{"July", "juli"}.
{"August", "augustus"}.
{"September", "september"}.
{"October", "oktober"}.
{"November", "november"}.
{"December", "december"}.
{"Room Configuration", "Instellingen van de chatruimte"}.
{"Room title", "Naam van de chatruimte"}.
% mod_muc/mod_muc.erl
{"Access denied by service policy", "De toegang werd geweigerd door het beleid van deze dienst"}.
{"Only service administrators are allowed to send service messages", "Alleen beheerders van deze dienst mogen mededelingen verzenden naar alle chatruimtes"}.
{"Room creation is denied by service policy", "De aanmaak van de chatruimte is verhinderd door de instellingen van deze server"}.
{"Conference room does not exist", "De chatruimte bestaat niet"}.
{"You need an x:data capable client to register nickname", "U hebt een client nodig die x:data ondersteunt om een bijnaam te registreren"}.
{"Nickname Registration at ", "Registratie van een bijnaam op "}.
{"Enter nickname you want to register", "Voer de bijnaam in die u wilt registreren"}.
{"Specified nickname is already registered", "De gekozen bijnaam is al geregistreerd"}.
{"You must fill in field \"Nickname\" in the form", "U moet het veld \"bijnaam\" invullen"}.
{"ejabberd MUC module\nCopyright (c) 2003-2006 Alexey Shchepin", "ejabberd's MUC module\nCopyright (c) 2003-2006 Alexey Shchepin"}.
% mod_muc/mod_muc_room.erl
{"Only moderators and participants are allowed to change subject in this room", "Alleen moderators en deelnemers mogen het onderwerp van deze chatruimte veranderen"}.
{"Only moderators are allowed to change subject in this room", "Alleen moderators mogen het onderwerp van deze chatruimte veranderen"}.
{"Visitors are not allowed to send messages to all occupants", "Bezoekers mogen geen berichten verzenden naar alle aanwezigen"}.
{"It is not allowed to send private messages to the conference", "Er mogen geen privéberichten naar de chatruimte worden verzonden"}.
{"It is not allowed to send normal messages to the conference", "Er mogen geen normale berichten naar de chatruimte worden verzonden"}.
{"Improper message type", "Onjuist berichttype"}.
{"Only occupants are allowed to send messages to the conference", "Alleen aanwezigen mogen berichten naar de chatruimte verzenden"}.
{"Nickname is already in use by another occupant", "Deze bijnaam is al in gebruik door een andere aanwezige"}.
{"Nickname is registered by another person", "Deze bijnaam is al geregistreerd door iemand anders"}.
{"It is not allowed to send private messages of type \"groupchat\"", "Er mogen geen privéberichten van het type \"groupchat\" worden verzonden"}.
{"Recipient is not in the conference room", "De ontvanger is niet in de chatruimte"}.
{"Only occupants are allowed to send queries to the conference", "Alleen aanwezigen mogen verzoeken verzenden naar de chatruimte"}.
{"Queries to the conference members are not allowed in this room", "Er mogen geen verzoeken verzenden worden naar deelnemers in deze chatruimte"}.
{"private, ", "privé, "}.
{"You have been banned from this room", "U werd verbannen uit deze chatruimte"}.
{"Membership required to enter this room", "U moet lid zijn om deze chatruimte te kunnen betreden"}.
{"This room is not anonymous", "Deze chatruimte is niet anoniem"}.
{"Password required to enter this room", "U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden"}.
{"Incorrect password", "Foutief wachtwoord"}.
{"Administrator privileges required", "U hebt beheerdersprivileges nodig"}.
{"Moderator privileges required", "U hebt moderatorprivileges nodig"}.
{"JID ~s is invalid", "De Jabber ID ~s is ongeldig"}.
{"Nickname ~s does not exist in the room", "De bijnaam ~s bestaat niet in deze chatruimte"}.
{"Invalid affiliation: ~s", "Ongeldige affiliatie: ~s"}.
{"Invalid role: ~s", "Ongeldige rol: ~s"}.
{"Owner privileges required", "U hebt eigenaarsprivileges nodig"}.
{"Configuration for ", "Instellingen van "}.
{"Make room persistent", "Chatruimte blijvend maken"}.
{"Make room public searchable", "Chatruimte doorzoekbaar maken"}.
{"Make participants list public", "Deelnemerslijst publiek maken"}.
{"Make room password protected", "Chatruimte beveiligen met een wachtwoord"}.
{"Make room semianonymous", "Chatruimte gedeeltelijk anoniem maken"}.
{"Make room members-only", "Chatruimte enkel toegankelijk maken voor leden"}.
{"Make room moderated", "Chatruimte gemodereerd maken"}.
{"Default users as participants", "Gebruikers standaard instellen als deelnemers"}.
{"Allow users to change subject", "Gebruikers mogen het onderwerp veranderen"}.
{"Allow users to send private messages", "Gebruikers mogen privéberichten verzenden"}.
{"Allow users to query other users", "Gebruikers mogen naar andere gebruikers verzoeken verzenden"}.
{"Allow users to send invites", "Gebruikers mogen uitnodigingen verzenden"}.
{"Enable logging", "Logs aanzetten"}.
{"You need an x:data capable client to configure room", "U hebt een client nodig die x:data ondersteunt om deze chatruimte in te stellen"}.
{"Description", "Beschrijving"}.
{"Number of occupants", "Aantal aanwezigen"}.
% mod_irc/mod_irc.erl
{"ejabberd IRC module\nCopyright (c) 2003-2006 Alexey Shchepin", "ejabberd's IRC-module\nCopyright (c) 2003-2006 Alexey Shchepin"}.
{"You need an x:data capable client to configure mod_irc settings", "U hebt een client nodig die x:data ondersteunt om dit IRC-transport in te stellen"}.
{"Registration in mod_irc for ", "Registratie van "}.
{"Enter username and encodings you wish to use for connecting to IRC servers", "Voer de gebruikersnaam en de coderingen in die u wilt gebruiken voor verbindingen met IRC-servers"}.
{"IRC Username", "Gebruikersnaam voor 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.", "Als u verschillende coderingen wilt opgeven voor elke IRC-server, ga dan voor elke server te werk op als volgt: '{\"IRC-server\", \"codering\"}'. Standaard gebruikt dit IRC-transport de codering \"~s\"."}.
{"Example: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}].", "Voorbeeld: [{\"irc.example.org\", \"koi8-r\"}, {\"vendetta.example.net\", \"iso8859-1\"}]."}.
{"Encodings", "Coderingen:"}.
% mod_adhoc.erl
{"Commands", "Commando's"}.
{"Ping", "Ping"}.
{"Pong", "Pong"}.
% mod_announce.erl
{"Really delete message of the day?", "Wilt u het bericht van de dag verwijderen?"}.
{"Subject", "Onderwerp"}.
{"Message body", "Bericht"}.
{"No body provided for announce message", "De mededeling bevat geen bericht"}.
{"Announcements", "Mededelingen"}.
{"Send announcement to all users", "Mededeling verzenden naar alle gebruikers"}.
{"Send announcement to all online users", "Mededeling verzenden naar alle online gebruikers"}.
{"Send announcement to all online users on all hosts", "Mededeling verzenden naar alle online gebruikers op alle virtuele hosts"}.
{"Set message of the day and send to online users", "Bericht van de dag instellen en verzenden naar online gebruikers"}.
{"Update message of the day (don't send)", "Bericht van de dag bijwerken (niet verzenden)"}.
{"Delete message of the day", "Bericht van de dag verwijderen"}.
% mod_pubsub/mod_pubsub.erl
{"ejabberd pub/sub module\nCopyright (c) 2003-2005 Alexey Shchepin", "ejabberd's pub/sub module\nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"ejabberd pub/sub module\nCopyright (c) 2003-2006 Alexey Shchepin", "ejabberd's pub/sub module\nCopyright (c) 2003-2006 Alexey Shchepin"}.
{[], "[]"}.
{"Node Creator", "Auteur van de node"}.
{"Deliver payloads with event notifications", "Berichten bezorgen samen met gebeurtenisnotificaties"}.
{"Notify subscribers when the node configuration changes", "Abonnees informeren wanneer de instellingen van de node gewijzigd worden"}.
{"Notify subscribers when the node configuration changes", "Abonnees informeren wanneer de instellingen van de node veranderen"}.
{"Notify subscribers when the node is deleted", "Abonnees informeren wanneer de node verwijderd word"}.
{"Notify subscribers when items are removed from the node", "Abonnees informeren wanneer items verwijderd worden uit de node"}.
{"Persist items to storage", "Items in geheugen bewaren"}.
{"Persist items to storage", "Items in het geheugen bewaren"}.
{"Max # of items to persist", "Maximum aantal in het geheugen te bewaren items"}.
{"Whether to allow subscriptions", "Abonnementsaanraag toestaan"}.
{"Specify the subscriber model", "Abonnementstype specificeren"}.
{"Specify the publisher model", "Publicatietype specificeren"}.
{"Whether to allow subscriptions", "Abonnementsaanvraag toestaan"}.
{"Specify the subscriber model", "Abonnementstype opgeven"}.
{"Specify the publisher model", "Publicatietype opgeven"}.
{"Max payload size in bytes", "Maximumgrootte van bericht in bytes"}.
{"Send items to new subscribers", "Items naar nieuwe abonnees verzenden"}.
{"Only deliver notifications to available users", "Notificaties alleen bezorgen aan beschikbare gebruikers"}.
{"Specify the current subscription approver", "Huidige verlener van abonnementen specificeren"}.
% 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"}.
{"Only service administrators are allowed to send service messages",
"Alleen service-beheerders kunnen service-berichten versturen"}.
{"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"}.
{"Room creation is denied by service policy", "De aanmaak van een kamer is verhinderd door de instellingen van deze server"}.
{"ejabberd MUC module\nCopyright (c) 2003-2005 Alexey Shchepin", "ejabberd's MUC module\nCopyright (c) 2003-2005 Alexey Shchepin"}.
% mod_muc/mod_muc_room.erl
{" has set the subject to: ", " veranderde het onderwerp in: "}.
{"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é, "}.
{"You need an x:data capable client to configure room", "U hebt een x:data-compatibele client nodig om deze kamer te configureren"}.
% mod_irc/mod_irc.erl
{"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.", "Als u verschillende coderingen wilt opgeven voor IRC-servers, geef dan in deze lijst waarden op 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"}.
{"ejabberd IRC module\nCopyright (c) 2003-2005 Alexey Shchepin", "ejabberd's IRC module\nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"Only deliver notifications to available users", "Notificaties alleen verzenden naar online gebruikers"}.
{"Specify the current subscription approver", "Huidige verlener van abonnementen opgeven"}.
% web/ejabberd_web_admin.erl
{"ejabberd administration", "Beheer"}.
{"Users", "Gebruikers"}.
{"ejabberd Web Interface", "ejabberd's webinterface"}.
{"Administration", "Beheer"}.
{"Virtual Hosts", "Virtuele hosts"}.
{"Nodes", "Nodes"}.
{"Statistics", "Statistieken"}.
{"(raw)", "(ruw)"}.
{"submitted", "verzonden"}.
{"bad format", "slecht formaat"}.
{"raw", "ruw"}.
{"ejabberd access control lists configuration", "Access control lists"}.
{"Delete Selected", "Geselecteerde verwijderen"}.
{"Submit", "Verzenden"}.
{"ejabberd access rules configuration", "Access rules"}.
{"~s access rule configuration", "Access rules op ~s"}.
{"ejabberd users", "Gebruikers"}.
{"ejabberd stats", "Statistieken"}.
{"Node not found", "Node niet gevonden"}.
{"Add New", "Nieuw toevoegen"}.
{"Registered users", "Geregistreerde gebruikers"}.
{"Authenticated 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 ", "Openstaande poorten op "}.
{"~p statistics", "Statistieken van ~p"}.
{"Uptime", "Uptime"}.
{"CPU Time", "Processortijd"}.
{"Transactions commited", "Bevestigde transacties"}.
{"Transactions aborted", "Afgebroken transacties"}.
{"Transactions restarted", "Herstarte transacties"}.
{"Transactions logged", "Gelogde transacties"}.
{"Port", "Poort"}.
{"Module", "Module"}.
{"Options", "Opties"}.
{"Update", "Bijwerken"}.
{"Delete", "Verwijderen"}.
{"Add User", "Gebruiker toevoegen"}.
{"Offline messages", "Offline berichten"}.
{"ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One", "ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One"}.
{"Users", "Gebruikers"}.
{"Last Activity", "Laatste activiteit"}.
{"Never", "Nooit"}.
{"~s offline messages queue", "offline berichten in de wachtrij op ~s"}.
{"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 "}.
{"Shared Roster", "Gedeeld roster"}.
{"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One", "ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One"}.
{"Online", "Online"}.
{"JID", "JID"}.
{"Validate", "Bevestigen"}.
{"Not found", "Niet gevonden"}.
{"Shared roster groups", "Gedeelde rostergroepen"}.
{"Name:", "Naam:"}.
{"Description:", "Beschrijving:"}.
{"Members:", "Leden:"}.
{"Displayed Groups:", "Weergegeven groepen:"}.
{"Group ", "Groep "}.
{"Users last activity", "Laatste activiteit van gebruikers"}.
{"(Raw)", "(Ruw)"}.
{"Submitted", "Verzonden"}.
{"Bad format", "Slecht formaat"}.
{"Submit", "Verzenden"}.
{"Raw", "Ruw"}.
{"Delete Selected", "Geselecteerde verwijderen"}.
{"~s access rule configuration", "Access rules op ~s"}.
{"ejabberd virtual hosts", "Virtuele hosts"}.
{"Users Last Activity", "Laatste activiteit van gebruikers"}.
{"Period: ", "Periode: "}.
{"Last month", "Afgelopen maand"}.
{"Last year", "Afgelopen jaar"}.
{"All activity", "Alle activiteit"}.
{"Show Ordinary Table", "Deel van tabel laten zien"}.
{"Show Integral Table", "Volledige tabel laten zien"}.
{"No data", "Geen gegevens"}.
{"Modules Management", "Modulebeheer"}.
{"Modules at ", "Modules op "}.
{"Start", "Starten"}.
{"Virtual Hosts", "Virtuele hosts"}.
{"ejabberd virtual hosts", "Virtuele hosts"}.
{"Node not found", "Node niet gevonden"}.
{"Add New", "Toevoegen"}.
{"Host", "Host"}.
{"ejabberd Web Interface", "ejabberd's webinterface"}.
{"Registered Users", "Geregistreerde gebruikers"}.
{"Add User", "Gebruiker toevoegen"}.
{"Offline Messages", "Offline berichten"}.
{"Never", "Nooit"}.
{"Online", "Online"}.
{"Registered Users:", "Geregistreerde gebruikers:"}.
{"Authenticated Users:", "Aangemelde gebruikers:"}.
{"Online Users:", "Online gebruikers:"}.
{"Outgoing s2s Connections:", "Uitgaande s2s-verbindingen:"}.
{"Outgoing s2s Servers:", "Uitgaande s2s-verbindingen:"}.
{"None", "Geen"}.
{"Change Password", "Wachtwoord wijzigen"}.
{"User ", "Gebruiker "}.
{"Connected Resources:", "Verbonden bronnen:"}.
{"Password:", "Wachtwoord:"}.
{"Offline Messages:", "Offline berichten:"}.
{"Roster", "Roster"}.
{"~s's Offline Messages Queue", "offline berichten van ~s"}.
{"Time", "Tijd"}.
{"From", "Van"}.
{"To", "Aan"}.
{"Packet", "Pakket"}.
{"Subscription", "Inschrijving"}.
{"Pending", "Bezig"}.
{"Groups", "Groepen"}.
{"Validate", "Bevestigen"}.
{"Remove", "Verwijderen"}.
{"Roster of ", "Roster van "}.
{"Add Jabber ID", "Jabber ID toevoegen"}.
{"No Data", "Geen gegevens"}.
{"Node ", "Node "}.
{"Listened Ports", "Openstaande poorten"}.
{"Update", "Bijwerken"}.
{"Restart", "Herstarten"}.
{"Stop", "Stoppen"}.
{"RPC Call Error", "RPC-oproepfout"}.
{"Database Tables at ", "Databasetabellen van "}.
{"Storage Type", "Opslagmethode"}.
{"Size", "Grootte"}.
{"Memory", "Geheugen"}.
{"Backup of ", "Backup maken van "}.
{"Remark that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.", "Merk op dat volgende opties enkel backups maken van de ingebouwde database Mnesia. Als u (een) andere database(s) gebruikt dan moet u daarvan (een) afzonderlijke backup(s) maken."}.
{"Store binary backup:", "Binaire backup maken:"}.
{"OK", "OK"}.
{"Restore binary backup immediately:", "Binaire backup direct herstellen:"}.
{"Restore binary backup after next ejabberd restart (requires less memory):", "Binaire backup herstellen na herstart van ejabberd (vereist minder geheugen):"}.
{"Store plain text backup:", "Backup naar een tekstbestand schrijven:"}.
{"Restore plain text backup immediately:", "Backup in een tekstbestand direct herstellen:"}.
{"Listened Ports at ", "Openstaande poorten op "}.
{"Modules at ", "Modules op "}.
{"Statistics of ~p", "Statistieken van ~p"}.
{"Uptime:", "Uptime:"}.
{"CPU Time:", "Processortijd:"}.
{"Transactions Commited:", "Bevestigde transacties:"}.
{"Transactions Aborted:", "Afgebroken transacties:"}.
{"Transactions Restarted:", "Herstarte transacties:"}.
{"Transactions Logged:", "Gelogde transacties:"}.
{"Update ", "Opwaarderen van "}.
{"Update plan", "Plan voor de opwaardering"}.
{"Updated modules", "Modules opwaarderen"}.
{"Update script", "Script voor de opwaardering"}.
{"Low level update script", "Lowlevel script voor de opwaardering"}.
{"Script check", "Controle van script"}.
{"Not Found", "Niet gevonden"}.
{"Port", "Poort"}.
{"Module", "Module"}.
{"Options", "Opties"}.
{"Delete", "Verwijderen"}.
{"Start", "Starten"}.
{"Shared Roster Groups", "Gedeelde rostergroepen"}.
{"Name:", "Naam:"}.
{"Description:", "Beschrijving:"}.
{"Members:", "Groepsleden:"}.
{"Displayed Groups:", "Weergegeven groepen:"}.
{"Group ", "Groep "}.
% mod_vcard_ldap.erl
{"Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin", "Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin", "ejabberd's vCard module\nCopyright (c) 2003-2005 Alexey Shchepin"}.
{"JID", "JID"}.
{"Given Name", "Opgegeven naam"}.
% ejabberd_c2s.erl
{"Use of STARTTLS required", "Gebruik van STARTTLS is vereist"}.
{"Replaced by new connection", "Vervangen door een nieuwe verbinding"}.
% Local Variables:
% mode: erlang
% End:
+139 -2
View File
@@ -297,8 +297,8 @@
{"Notify subscribers when the node configuration changes", "Informuj subskrybentów gdy konfiguracja gałęzi się zmieni"}.
{"Notify subscribers when the node is deleted", "Informuj subskrybentów gdy gałąż zostanie wykasowana"}.
{"Notify subscribers when items are removed from the node", "Informuj subskrybentów kiedy zostaną z gałęzi usunięte jakieś elementy"}.
{"Persist items to storage", "Przechowuj przedmioty pub/sub w pami?"}.
{"Max # of items to persist", "Maksymalna ilość przechowywanych przedmiot?"}.
{"Persist items to storage", "Przechowuj przedmioty pub/sub w pamięci"}.
{"Max # of items to persist", "Maksymalna ilość przechowywanych przedmiotów"}.
{"Whether to allow subscriptions", "Czy pozwolić na subskrypcje"}.
{"Specify the subscriber model", "Oznacz model subskrybenta"}.
{"Specify the publisher model", "Oznacz model publikującego"}.
@@ -324,3 +324,140 @@
% Local Variables:
% mode: erlang
% End:
% /usr/home/src/ejabberd/ejabberd/src/mod_vcard_odbc.erl
{"Erlang Jabber Server\nCopyright (c) 2002-2006 Alexey Shchepin", ""}.
{"Email", ""}.
{"ejabberd vCard module\nCopyright (c) 2003-2006 Alexey Shchepin", ""}.
{"Search Results for ", "Wyniki wyszukiwania dla "}.
{"Jabber ID", ""}.
% /usr/home/src/ejabberd/ejabberd/src/mod_adhoc.erl
{"Commands", "Polecenia"}.
{"Ping", "Ping"}.
{"Pong", "Pong"}.
% /usr/home/src/ejabberd/ejabberd/src/ejabberd_c2s.erl
{"Replaced by new connection", "Podmienione przez nowe połączenie"}.
% /usr/home/src/ejabberd/ejabberd/src/mod_announce.erl
{"Really delete message of the day?", "Na pewno usunąć wiadomość dnia?"}.
{"Subject", "Temat"}.
{"Message body", "Treść wiadomości"}.
{"No body provided for announce message", "Nikt nie jest uprawniony do rozsyłania oznajmień"}.
{"Announcements", "Oznajmienia"}.
{"Send announcement to all users", "Wyślij oznajmienie do wszystkich użytkowników"}.
{"Send announcement to all online users", "Wyślij oznajmienie do wszystkich użytkowników online"}.
{"Send announcement to all online users on all hosts", "Wyślij oznajmienie do wszystkich użytkowników online na wszystkich hostach"}.
{"Set message of the day and send to online users", "Wyślij wiadomość dnia do wszystkich użytkowników online"}.
{"Update message of the day (don't send)", "Zmień wiadomość dnia (nie wysyłaj)"}.
{"Delete message of the day", "Usuń wiadomość dnia"}.
% /usr/home/src/ejabberd/ejabberd/src/mod_configure.erl
{"Database", "Baza"}.
{"Outgoing s2s Connections", "Wychodzące połączenia s2s"}.
{"Import Users From jabberd 1.4 Spool Files", "Importuj użytkowników z plików spool serwera jabber 1.4"}.
{"Database Tables Configuration at ", "Konfiguracja tabel bazy na "}.
% /usr/home/src/ejabberd/ejabberd/src/mod_pubsub/mod_pubsub.erl
{"ejabberd pub/sub module\nCopyright (c) 2003-2006 Alexey Shchepin", ""}.
% /usr/home/src/ejabberd/ejabberd/src/web/ejabberd_web_admin.erl
{"ejabberd Web Interface", "Interfejs WWW serwera ejabberd"}.
{"Administration", "Administracja"}.
{"ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One", ""}.
{"(Raw)", ""}.
{"Submitted", "Wprowadzone"}.
{"Bad format", "Zły format"}.
{"Raw", ""}.
{"Users Last Activity", "Ostatnia aktywność użytkowników"}.
{"Registered Users", "Użytkownicy zarejestrowani"}.
{"Offline Messages", "Wiadomości offline"}.
{"Registered Users:", "Użytkownicy zarejestrowani:"}.
{"Authenticated Users:", "Użytkownicy autoryzowani:"}.
{"Online Users:", "Użytkownicy online:"}.
{"Outgoing s2s Connections:", "Wychodzące połączenia s2s:"}.
{"Outgoing s2s Servers:", "Serwery zewnętrzne s2s:"}.
{"Offline Messages:", "Wiadomości offline"}.
{"~s's Offline Messages Queue", "~s skolejkowanych wiadomości offline"}.
{"Add Jabber ID", "Dodaj JID"}.
{"No Data", "Brak danych"}.
{"Listened Ports", "Porty nasłuchujące"}.
{"RPC Call Error", "Błąd odwołania RPC"}.
{"Database Tables at ", "Tabele bazy na "}.
{"Backup of ", "Kopia zapasowa "}.
{"Remark that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.", "Te opcje backupują jedynie bazę Mnesia. Jeśli używasz modułu ODBC - musisz wykonać kopię SQL oddzielnie"}.
{"Store binary backup:", "Zachowaj kopię binarną:"}.
{"Restore binary backup immediately:", "Natychmiast odtwórz kopię binarną:"}.
{"Restore binary backup after next ejabberd restart (requires less memory):", "Odtwórz kopię binarną podczas następnego restaru ejabberd-a (wymaga mniej pamięci)"}.
{"Store plain text backup:", "Zachowaj kopię w czystym tekście"}.
{"Restore plain text backup immediately:", "Odtwórz kopię z czystego tekstu"}.
{"Statistics of ~p", "Statystyki ~p"}.
{"Uptime:", "Uptime"}.
{"CPU Time:", "Czas CPU"}.
{"Transactions Commited:", "Transakcje zakończone"}.
{"Transactions Aborted:", "Transakcje anulowane"}.
{"Transactions Restarted:", "Transakcje uruchomione ponownie"}.
{"Transactions Logged:", "Transakcje logowane"}.
{"Update ", "Uaktualnij"}.
{"Update plan", "Uaktualnij plan"}.
{"Updated modules", "Uaktualnione moduły"}.
{"Update script", "Uaktualnij skrypt"}.
{"Low level update script", ""}.
{"Script check", "Sprawdź skrypt"}.
{"Not Found", "Nie znaleziono"}.
{"Shared Roster Groups", "Grupy współdzielone"}.
% /usr/home/src/ejabberd/ejabberd/src/mod_irc/mod_irc.erl
{"ejabberd IRC module\nCopyright (c) 2003-2006 Alexey Shchepin", ""}.
% /usr/home/src/ejabberd/ejabberd/src/mod_muc/mod_muc_log.erl
{"Chatroom configuration modified", "Konfiguracja pokoju zmodyfikowana"}.
{"joins the room", "dołączył(a) się do pokoju"}.
{"leaves the room", "opóścił(a) pokój"}.
{"has been kicked", "został(a) kopnięty(a)"}.
{"has been banned", "został(a) zabanowany(a)"}.
{"is now known as", "jest teraz znany(a) jako"}.
{"Monday", "Poniedziałek"}.
{"Tuesday", "Wtorek"}.
{"Wednesday", "Środa"}.
{"Thursday", "Czwartek"}.
{"Friday", "Piątek"}.
{"Saturday", "Sobota"}.
{"Sunday", "Niedziela"}.
{"January", "Styczeń"}.
{"February", "Luty"}.
{"March", "Marzec"}.
{"April", "Kwiecień"}.
{"May", "Maj"}.
{"June", "Czerwiec"}.
{"July", "Lipiec"}.
{"August", "Sierpień"}.
{"September", "Wrzesień"}.
{"October", "Październik"}.
{"November", "Listopad"}.
{"December", "Grudzień"}.
{"Room Configuration", "Konfiguracja pokoju"}.
% /usr/home/src/ejabberd/ejabberd/src/mod_muc/mod_muc.erl
{"You must fill in field \"Nickname\" in the form", "Musisz wypełnić pole NICKNAME w formularzu"}.
{"ejabberd MUC module\nCopyright (c) 2003-2006 Alexey Shchepin", ""}.
% /usr/home/src/ejabberd/ejabberd/src/mod_muc/mod_muc_room.erl
{"This room is not anonymous", "Pokój nie jest nieznany"}.
{"Make room persistent", "Utwórz pokój na stałe"}.
{"Make room public searchable", "Pozwól wyszukiwać pokój"}.
{"Make participants list public", "Upublicznij listę uczestników"}.
{"Make room password protected", "Zabezpiecz pokój hasłem"}.
{"Make room semianonymous", "Utwórz pokój pół-nieznanym"}.
{"Make room members-only", "Utwórz pokój tylko dla członków"}.
{"Make room moderated", "Moderuj pokój"}.
{"Default users as participants", "Domyślni użytkownicy jako uczestnicy"}.
{"Allow users to change subject", "Pozwól użytkownikom zmienić tytuł pokoju"}.
{"Allow users to send private messages", "Pozwól użytkownikom wysyłać prywatne wiadomości"}.
{"Allow users to query other users", "Pozwól użytkownikom pobierać informacje o innych użytkownikach"}.
{"Allow users to send invites", "Pozwól użytkownikom wysyłać zaproszenia"}.
{"Enable logging", "Włącz logowanie"}.
{"Description", "Opis"}.
{"Number of occupants", "Liczba uczestników"}.
+151 -7
View File
@@ -1,6 +1,6 @@
% $Id$
% Brazilian Portuguese translation
% Author: Victor Hugo dos Santos
% Authors: Victor Hugo dos Santos, Lucius Curado
@@ -73,9 +73,9 @@
{"Birthday","Aniversário"}.
{"City", "Cidade"}.
{"Country","País"}.
{"ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo vCard para ejabberd\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"ejabberd vCard module\nCopyright (c) 2003-2006 Alexey Shchepin", "Módulo vCard para ejabberd\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"email", "e-mail"}.
{"Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin", "Servidor Jabber em Erlang\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"Erlang Jabber Server\nCopyright (c) 2002-2006 Alexey Shchepin", "Servidor Jabber em Erlang\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"Family Name", "Nome de família"}.
{"Fill in fields to search for any matching Jabber User", "Preencha os campos para procurar usuários Jabber coincidentes"}.
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)", "Preencha o formulário para buscar usuários Jabber. Agrega * ao final de um campo para buscar sub-palavras."}.
@@ -94,7 +94,7 @@
{[], " "}.
{"Deliver payloads with event notifications", "Enviar payloads junto com as notificações de eventos"}.
{"ejabberd pub/sub module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo Pub/Sub para ejabberd\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"ejabberd pub/sub module\nCopyright (c) 2003-2006 Alexey Shchepin", "Módulo Pub/Sub para ejabberd\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"Max # of items to persist", "Máximo # de elementos que persistem"}.
{"Max payload size in bytes", "Máximo tamanho do payload em bytes"}.
{"Node Creator", "Criador do nodo"}.
@@ -118,7 +118,7 @@
{"Enable logging?", "Salvar históricos?"}.
{"Access denied by service policy", "Aceso denegado por la política do serviço"}.
{"Conference room does not exist", "La sala de conferencias não existe"}.
{"ejabberd MUC module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo de MUC para ejabbed\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"ejabberd MUC module\nCopyright (c) 2003-2006 Alexey Shchepin", "Módulo de MUC para ejabbed\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"Enter nickname you want to register", "Introduza o apelido que quer registrar"}.
{"Incorrect password", "Senha incorreta"}.
{"Make room anonymous?", "Tornar a sala anônima?"}.
@@ -181,7 +181,7 @@
% mod_irc/mod_irc.erl
{"ejabberd IRC module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo de IRC para ejabberd\nCopyright (c) 2002-2005 Alexey Shchepin"}.
{"ejabberd IRC module\nCopyright (c) 2003-2006 Alexey Shchepin", "Módulo de IRC para ejabberd\nCopyright (c) 2002-2006 Alexey Shchepin"}.
{"Encodings", "Codificação"}.
{"Enter username and encodings you wish to use for connecting to IRC servers", "Introduza o nome de usuário e codificações de caracteres que quer usar ao conectar-se aos servidores de IRC"}.
{"Example: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}].", "Exemplo: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}]."}.
@@ -209,7 +209,7 @@
{"ejabberd access control lists configuration", "Configuração das Listas de Controle de Acesso do ejabberd"}.
{"ejabberd access rules configuration", "Configuração de las Regras de Aceso de ejabberd"}.
{"ejabberd administration", "Administração de ejabberd"}.
{"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One", "ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One"}.
{"ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One", "ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One"}.
{"ejabberd stats", "Estatísticas de ejabberd"}.
{"ejabberd users", "Usuários do ejabberd"}.
{"ejabberd virtual hosts", "Maquinas virtuais de ejabberd"}.
@@ -292,6 +292,150 @@
{"JID", "JID"}.
% mod_adhoc.erl
{"Commands", "Comandos"}.
{"Ping", "Ping"}.
{"Pong", "Pong"}.
% mod_vcard_odbc.erl
{"Email", "e-mail"}.
{"Search Results for ", "Resultados de pesquisa para"}.
{"Jabber ID", "ID Jabber"}.
% ejabberd_c2s.erl
{"Replaced by new connection", "Substituído por nova conexão"}.
% mod_announce.erl
{"Really delete message of the day?", "Deletar realmente a mensagem do dia?"}.
{"Subject", "Assunto"}.
{"Message body", "Corpo da mensagem"}.
{"No body provided for announce message", "Nenhum corpo de texto fornecido para anunciar mensagem"}.
{"Announcements", "Anúncios"}.
{"Send announcement to all users", "Enviar anúncio a todos os usuários"}.
{"Send announcement to all online users", "Enviar anúncio a todos os usuárions online"}.
{"Send announcement to all online users on all hosts", "Enviar anúncio a todos usuários online em todas as máquinas"}.
{"Set message of the day and send to online users", "Definir mensagem do dia e enviar a todos usuários online"}.
{"Update message of the day (don't send)", "Atualizar mensagem do dia (não enviar)"}.
{"Delete message of the day", "Apagar mensagem do dia"}.
% mod_configure.erl
{"Database", "Base de dados"}.
{"Host Name", "Nome da máquina"}.
{"Outgoing s2s Connections", "Conexões que partam de s2s"}.
{"Import Users From jabberd 1.4 Spool Files", "Importar usuários de arquivos jabberd 1.4"}.
{"Database Tables Configuration at ", "Configuração de Tabelas de Base de dados em "}.
{"Stop Modules at ", "Parar módulos em "}.
{"List of modules to start", "Listas de módulos para inicializar"}.
{"Hostname Configuration", "Configuração do Hostname"}.
{"Choose host name", "Definir nome da máquina"}.
{"Host name", "Nome da máquina"}.
% mod_pubsub/mod_pubsub.erl
% web/ejabberd_web_admin.erl
{"ejabberd Web Interface", "Interface Web do ejabberd"}.
{"Administration", "Administração"}.
{"Nodes", "Nós"}.
{"(Raw)", "(Intocado)"}.
{"Submitted", "Submetido"}.
{"Bad format", "Formato incorreto"}.
{"Raw", "Intocado"}.
{"Users Last Activity", "Ultimas atividades dos usuários"}.
{"Node not found", "Nó não encontrado"}.
{"Registered Users", "Usuários Registrados"}.
{"Offline Messages", "Mensagens offline"}.
{"Registered Users:", "Usuários registrados"}.
{"Authenticated Users:", "Usuários Autenticados"}.
{"Online Users:", "Usuários online"}.
{"Outgoing s2s Connections:", "Conexões que partem de s2s"}.
{"Outgoing s2s Servers:", "Servidores que partem de s2s"}.
{"None", "Nenhum"}.
{"Offline Messages:", "Mensagens offline"}.
{"~s's Offline Messages Queue", "~s's Fila de Mensagens Offline"}.
{"Add Jabber ID", "Adicionar ID jabber"}.
{"No Data", "Nenhum dado"}.
{"Node ", "Nó"}.
{"Listened Ports", "Portas escutadas"}.
{"RPC Call Error", "Erro de chamada RPC"}.
{"Database Tables at ", "Tabelas de base de dados em"}.
{"Backup of ", "Backup de"}.
{"Remark that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.", "Observe que tais opções farão backup apenas da base de dados Mnesia. Caso você esteja utilizando o modulo ODBC, você precisará fazer backup de sua base de dados SQL separadamente."}.
{"Store binary backup:", "Armazenar backup binário:"}.
{"OK", "OK"}.
{"Restore binary backup immediately:", "Restaurar backup binário imediatamente"}.
{"Restore binary backup after next ejabberd restart (requires less memory):", "Restaurar backup binário após próximo reinicialização do ejabberd (requer menos memória):"}.
{"Store plain text backup:", "Armazenar backup plain text"}.
{"Restore plain text backup immediately:", "Restaurar backup plain text imediatamente:"}.
{"Statistics of ~p", "Estatísticas de ~p"}.
{"Uptime:", "Uptime:"}.
{"CPU Time:", "Tempo de CPU"}.
{"Transactions Commited:", "Transações:"}.
{"Transactions Aborted:", "Transações abortadas:"}.
{"Transactions Restarted:", "Transações restauradas:"}.
{"Transactions Logged:", "Transações de log:"}.
{"Update ", "Atualizar"}.
{"Update plan", "Plano de Atualização"}.
{"Updated modules", "Módulos atualizados"}.
{"Update script", "Script de atualização"}.
{"Low level update script", "Script de atualização low level"}.
{"Script check", "Verificação de Script"}.
{"Not Found", "Não encontrado"}.
{"Shared Roster Groups", "Grupos Shared Roster"}.
{"Description:", "Descrição:"}.
{"Displayed Groups:", "Grupos Indicados"}.
% mod_irc/mod_irc.erl
% mod_muc/mod_muc_log.erl
{"Chatroom configuration modified", "Configuração da sala de bate-papo modificada"}.
{"joins the room", "Entrar na sala"}.
{"leaves the room", "Sair da sala"}.
{"has been kicked", "foi removido"}.
{"has been banned", "foi banido"}.
{"is now known as", ""}.
{"Monday", "Segunda"}.
{"Tuesday", "Terça"}.
{"Wednesday", "Quarta"}.
{"Thursday", "Quinta"}.
{"Friday", "Sexta"}.
{"Saturday", "Sábado"}.
{"Sunday", "Domingo"}.
{"January", "Janeiro"}.
{"February", "Fevereiro"}.
{"March", "Março"}.
{"April", "Abril"}.
{"May", "Maio"}.
{"June", "Junho"}.
{"July", "Julho"}.
{"August", "Agosto"}.
{"September", "Setembro"}.
{"October", "Outubro"}.
{"November", "Novembro"}.
{"December", "Dezembro"}.
{"Room Configuration", "Configuração de salas"}.
% mod_muc/mod_muc.erl
{"Only service administrators are allowed to send service messages", "Apenas administradores possuem permissão para enviar mensagens de serviço"}.
{"You must fill in field \"Nickname\" in the form", "Você deve completar o campo \"Apelido\" no formulário"}.
% mod_muc/mod_muc_room.erl
{"This room is not anonymous", "Essa sala não é anônima"}.
{"Make room persistent", "Tornar sala persistente"}.
{"Make room public searchable", "Tornar sala pública possível de ser encontrada"}.
{"Make participants list public", "Tornar pública a lista de participantes"}.
{"Make room password protected", "Tornar protegida a senha da sala"}.
{"Make room semianonymous", "Tornar sala semi-anônima"}.
{"Make room members-only", "Tornar sala apenas para membros"}.
{"Make room moderated", "Tornar a sala moderada"}.
{"Default users as participants", "Usuários padrões como participantes"}.
{"Allow users to change subject", "Permitir a usuários modificar o assunto"}.
{"Allow users to send private messages", "Permitir a usuários enviarem mensagens privadas"}.
{"Allow users to query other users", "Permitir a usuários pesquisar informações sobre os demais"}.
{"Allow users to send invites", "Permitir a usuários envio de convites"}.
{"Enable logging", "Permitir criação de logs"}.
{"Description", "Descrição"}.
{"Number of occupants", "Número de participantes"}.
% Local Variables:
% mode: erlang
% End:
+132 -76
View File
@@ -9,8 +9,28 @@
{"Illegal resource format", "Неправильный формат ресурса"}.
{"Resource conflict", "Конфликт ресурсов"}.
% mod_adhoc.erl
{"Commands", "Команды"}.
{"Ping", "Пинг"}.
{"Pong", "Понг"}.
% mod_announce.erl
{"Really delete message of the day?", "Действительно удалить сообщение дня?"}.
{"Subject", "Тема"}.
{"Message body", "Тело сообщения"}.
{"No body provided for announce message", "Тело объявления не должно быть пустым"}.
{"Announcements", "Объявления"}.
{"Send announcement to all users", "Разослать объявление всем пользователям"}.
{"Send announcement to all online users", "Разослать объявление всем подключённым пользователям"}.
{"Send announcement to all online users on all hosts",
"Разослать объявление всем подключённым пользователям на всех виртуальных серверах"}.
{"Set message of the day and send to online users",
"Установить сообщение дня и разослать его подключённым пользователям"}.
{"Update message of the day (don't send)", "Обновить сообщение дня (не рассылать)"}.
{"Delete message of the day", "Удалить сообщение дня"}.
% mod_configure.erl
{"DB Tables Configuration at ", "Конфигурация таблиц БД на "}.
{"Database Tables Configuration at ", "Конфигурация таблиц базы данных на "}.
{"Choose storage type of tables", "Выберите тип хранения таблиц"}.
{"RAM copy", "ОЗУ"}.
{"RAM and disc copy", "ОЗУ и диск"}.
@@ -50,7 +70,7 @@
{"Configuration", "Конфигурация"}.
{"Online Users", "Подключённые пользователи"}.
{"All Users", "Все пользователи"}.
{"Outgoing S2S connections", "Исходящие S2S-соединения"}.
{"Outgoing s2s Connections", "Исходящие s2s-соединения"}.
{"To ~s", "К ~s"}.
{"From ~s", "От ~s"}.
{"Running Nodes", "Работающие узлы"}.
@@ -59,12 +79,12 @@
{"Access Control Lists", "Списки управления доступом"}.
{"Access Rules", "Правила доступа"}.
{"Remove Users", "Удаление пользователей"}.
{"DB", "БД"}.
{"Database", "База данных"}.
{"Modules", "Модули"}.
{"Start Modules", "Запуск модулей"}.
{"Stop Modules", "Остановка модулей"}.
{"Backup Management", "Управление резервным копированием"}.
{"Import users from jabberd1.4 spool files", "Импорт пользователей из спула jabberd1.4"}.
{"Import Users From jabberd 1.4 Spool Files", "Импорт пользователей из спула jabberd 1.4"}.
{"Backup", "Резервное копирование"}.
{"Restore", "Восстановление из резервной копии"}.
{"Dump to Text File", "Копирование в текстовый файл"}.
@@ -76,16 +96,17 @@
"Выберите имя пользователя и пароль для регистрации на этом сервере"}.
% mod_vcard.erl
{"Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin",
"Erlang Jabber Server\nCopyright (c) 2002-2005 Алексей Щепин"}.
{"ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin",
"ejabberd vCard модуль\nCopyright (c) 2003-2005 Алексей Щепин"}.
{"Erlang Jabber Server\nCopyright (c) 2002-2006 Alexey Shchepin",
"Erlang Jabber Server\nCopyright (c) 2002-2006 Алексей Щепин"}.
{"ejabberd vCard module\nCopyright (c) 2003-2006 Alexey Shchepin",
"ejabberd vCard модуль\nCopyright (c) 2003-2006 Алексей Щепин"}.
{"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 ", "Результаты поиска в "}.
{"Search Results for ", "Результаты поиска в "}.
{"Jabber ID", "Jabber ID"}.
{"User", "Пользователь"}.
{"Full Name", "Полное имя"}.
{"Name", "Имя"}.
@@ -95,7 +116,7 @@
{"Birthday", "День рождения"}.
{"Country", "Страна"}.
{"City", "Город"}.
{"email", "email"}.
{"Email", "Электронная почта"}.
{"Organization Name", "Название организации"}.
{"Organization Unit", "Отдел организации"}.
@@ -104,8 +125,8 @@
{"Given Name", "Имя"}.
% mod_pubsub/mod_pubsub.erl
{"ejabberd pub/sub module\nCopyright (c) 2003-2005 Alexey Shchepin",
"ejabberd pub/sub модуль\nCopyright (c) 2003-2005 Алексей Щепин"}.
{"ejabberd pub/sub module\nCopyright (c) 2003-2006 Alexey Shchepin",
"ejabberd pub/sub модуль\nCopyright (c) 2003-2006 Алексей Щепин"}.
{"Node Creator", "Создатель сборника"}.
{"Deliver payloads with event notifications", "Доставлять вместе с уведомлениями o публикациях сами публикации"}.
{"Notify subscribers when the node configuration changes", "Уведомлять подписчиков об изменении конфигурации сборника"}.
@@ -126,43 +147,72 @@
"Чтобы зарегистрировать псевдоним, требуется x:data-совместимый клиент"}.
{"Nickname Registration at ", "Регистрация псевдонима на "}.
{"Enter nickname you want to register", "Введите псевдоним, который Вы хотели бы зарегистрировать"}.
{"ejabberd MUC module\nCopyright (c) 2003-2005 Alexey Shchepin",
"ejabberd MUC модуль\nCopyright (c) 2003-2005 Алексей Щепин"}.
{"ejabberd MUC module\nCopyright (c) 2003-2006 Alexey Shchepin",
"ejabberd MUC модуль\nCopyright (c) 2003-2006 Алексей Щепин"}.
{"Only service administrators are allowed to send service messages",
"Только администратор службы может посылать служебные сообщения"}.
{"Room creation is denied by service policy",
"Cоздавать конференцию запрещено политикой службы"}.
{"Conference room does not exist", "Конференция не существует"}.
{"Access denied by service policy", "Доступ запрещён политикой службы"}.
{"You must fill in field \"nick\" in the form",
"Вы должны заполнить поле \"nick\" в форме"}.
{"You must fill in field \"Nickname\" in the form",
"Вы должны заполнить поле \"Псевдоним\" в форме"}.
{"Specified nickname is already registered", "Указанный псевдоним уже зарегистрирован"}.
% /home/sergei/src/ejabberd/ejabberd/src/mod_muc/mod_muc_log.erl
{"Chatroom configuration modified", "Конфигурация комнаты изменилась"}.
{"joins the room", "вошёл(а) в комнату"}.
{"leaves the room", "вышел(а) из комнаты"}.
{"has been kicked", "выгнали из комнаты"}.
{"has been banned", "запретили входить в комнату"}.
{"is now known as", "изменил(а) имя на"}.
{"Monday", "Понедельник"}.
{"Tuesday", "Вторник"}.
{"Wednesday", "Среда"}.
{"Thursday", "Четверг"}.
{"Friday", "Пятница"}.
{"Saturday", "Суббота"}.
{"Sunday", "Воскресенье"}.
{"January", "января"}.
{"February", "февраля"}.
{"March", "марта"}.
{"April", "апреля"}.
{"May", "мая"}.
{"June", "июня"}.
{"July", "июля"}.
{"August", "августа"}.
{"September", "сентября"}.
{"October", "октября"}.
{"November", "ноября"}.
{"December", "декабря"}.
{"Room Configuration", "Конфигурация комнаты"}.
% mod_muc/mod_muc_room.erl
{"This room is not anonymous", "Эта комната не анонимная"}.
{" 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?", "Сделать комнату защищённой паролем?"}.
{"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 participants",
"Сделать пользователей участниками по умолчанию"}.
{"Make room members-only",
"Комната только для зарегистрированных участников"}.
{"Allow users to send invites",
"Разрешить пользователям посылать приглашения"}.
{"Make room password protected", "Сделать комнату защищённой паролем"}.
{"Password", "Пароль"}.
{"Make room anonymous?", "Сделать комнату анонимной?"}.
{"Enable logging?", "Включить журналирование?"}.
{"Make room semianonymous", "Сделать комнату полуанонимной"}.
{"Enable logging", "Включить журналирование"}.
{"Only moderators and participants are allowed to change subject in this room",
"Только модераторы и участники могут изменять тему в этой комнате"}.
{"Only moderators are allowed to change subject in this room",
@@ -197,10 +247,12 @@
{"Invalid role: ~s", "Недопустимая роль: ~s"}.
{"Owner privileges required", "Требуются права владельца"}.
{"private, ", "приватная, "}.
{"Description", "Описание"}.
{"Number of occupants", "Число присутствующих"}.
% mod_irc/mod_irc.erl
{"ejabberd IRC module\nCopyright (c) 2003-2005 Alexey Shchepin",
"ejabberd IRC модуль\nCopyright (c) 2003-2005 Алексей Щепин"}.
{"ejabberd IRC module\nCopyright (c) 2003-2006 Alexey Shchepin",
"ejabberd IRC модуль\nCopyright (c) 2003-2006 Алексей Щепин"}.
{"You need an x:data capable client to configure mod_irc settings",
"Чтобы настроить параметры mod_irc, требуется x:data-совместимый клиент"}.
{"Registration in mod_irc for ", "Регистрация в mod_irc для "}.
@@ -213,93 +265,98 @@
% web/ejabberd_web_admin.erl
{"ejabberd Web Interface", "Web-интерфейс ejabberd"}.
{"ejabberd administration", "Администрирование ejabberd"}.
{"Administration", "Администрирование"}.
{"Users", "Пользователи"}.
{"Nodes", "Узлы"}.
{"Statistics", "Статистика"}.
{"(raw)", "(необработанный формат)"}.
{"submitted", "отправлено"}.
{"bad format", "неправильный формат"}.
{"raw", "необработанный формат"}.
{"ejabberd access control lists configuration", "Конфигурация списков управления доступом ejabberd"}.
{"(Raw)", "(Необработанный формат)"}.
{"Submitted", "Отправлено"}.
{"Bad format", "Неправильный формат"}.
{"Raw", "Необработанный формат"}.
{"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", "Зарегистрированные пользователи"}.
{"Authenticated users", "Аутентифицированные пользователи"}.
{"Online users", "Подключённые пользователи"}.
{"Outgoing S2S servers", "Исходящие S2S-серверы"}.
{"Registered Users", "Зарегистрированные пользователи"}.
{"Registered Users:", "Зарегистрированные пользователи:"}.
{"Authenticated Users:", "Аутентифицированные пользователи:"}.
{"Online Users", "Подключённые пользователи"}.
{"Online Users:", "Подключённые пользователи:"}.
{"Outgoing s2s Connections:", "Исходящие s2s-серверы:"}.
{"Outgoing s2s Servers:", "Исходящие s2s-серверы:"}.
{"Change Password", "Сменить пароль"}.
{"Connected Resources:", "Подключённые ресурсы:"}.
{"Password:", "Пароль:"}.
{"None", "Нет"}.
{"Node ", "Узел "}.
{"DB Management", "Управление БД"}.
{"Listened Ports Management", "Управление прослушиваемыми портами"}.
{"Listened Ports", "Прослушиваемые порты"}.
{"Restart", "Перезапустить"}.
{"Stop", "Остановить"}.
{"RPC call error", "Ошибка вызова RPC"}.
{"DB Tables at ", "Таблицы БД на "}.
{"RPC Call Error", "Ошибка вызова RPC"}.
{"Database Tables at ", "Таблицы базы данных на "}.
{"Name", "Название"}.
{"Storage Type", "Тип таблицы"}.
{"Size", "Размер"}.
{"Memory", "Память"}.
{"Backup Management at ", "Управление резервным копированием на "}.
{"Store a backup in a file", "Сохранить резервную копию в файл"}.
{"Backup of ", "Резервное копирование "}.
{"Remark that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.", "Заметьте, что здесь производится резервное копирование только встроенной базы данных Mnesia. Если Вы также используете другое хранилище данных (например с помощью модуля ODBC), то его резервное копирование следует осуществлять отдельно."}.
{"Store binary backup:", "Сохранить бинарную резервную копию:"}.
{"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", "Восстановить базу данных из текстового файла"}.
{"Restore binary backup immediately:", "Восстановить из бинарной резервной копии немедленно:"}.
{"Restore binary backup after next ejabberd restart (requires less memory):", "Восстановить из бинарной резервной копии при следующем запуске (требует меньше памяти):"}.
{"Store plain text backup:", "Сохранить текстовую резервную копию:"}.
{"Restore plain text backup immediately:", "Восстановить из текстовой резервной копии немедленно:"}.
{"Listened Ports at ", "Прослушиваемые порты на "}.
{"~p statistics", "статистика узла ~p"}.
{"Uptime", "Время работы"}.
{"CPU Time", "Процессорное время"}.
{"Transactions commited", "Транзакции завершенные"}.
{"Transactions aborted", "Транзакции отмененные"}.
{"Transactions restarted", "Транзакции перезапущенные"}.
{"Transactions logged", "Транзакции запротоколированные"}.
{"Statistics of ~p", "статистика узла ~p"}.
{"Uptime:", "Время работы:"}.
{"CPU Time:", "Процессорное время:"}.
{"Transactions Commited:", "Транзакции завершенные:"}.
{"Transactions Aborted:", "Транзакции отмененные:"}.
{"Transactions Restarted:", "Транзакции перезапущенные:"}.
{"Transactions Logged:", "Транзакции запротоколированные:"}.
{"Update ", "Обновление "}.
{"Update plan", "План обновления"}.
{"Updated modules", "Обновлённые модули"}.
{"Update script", "Сценарий обновления"}.
{"Low level update script", "Низкоуровневый сценарий обновления"}.
{"Script check", "Проверка сценария"}.
{"Port", "Порт"}.
{"Module", "Модуль"}.
{"Options", "Параметры"}.
{"Update", "Обновить"}.
{"Delete", "Удалить"}.
{"Add User", "Добавить пользователя"}.
{"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One", "ejabberd (c) 2002-2005 Алексей Щепин, 2004-2005 Process One"}.
{"Offline messages", "Офлайновые сообщения"}.
{"ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One", "ejabberd (c) 2002-2006 Алексей Щепин, 2004-2006 Process One"}.
{"Offline Messages", "Офлайновые сообщения"}.
{"Offline Messages:", "Офлайновые сообщения:"}.
{"Last Activity", "Последнее подключение"}.
{"Never", "Никогда"}.
{"~s offline messages queue", "Oчередь офлайновых сообщений ~s"}.
{"~s's Offline Messages Queue", "Oчередь офлайновых сообщений ~s"}.
{"Time", "Время"}.
{"From", "От кого"}.
{"To", "Кому"}.
{"Packet", "Пакет"}.
{"Offline messages:", "Офлайновые сообщения:"}.
{"Roster", "Ростер"}.
{"Nickname", "Псевдоним"}.
{"Subscription", "Подписка"}.
{"Pending", "Ожидание"}.
{"Groups", "Группы"}.
{"Remove", "Удалить"}.
{"Add JID", "Добавить JID"}.
{"Add Jabber ID", "Добавить Jabber ID"}.
{"User ", "Пользователь "}.
{"Roster of ", "Ростер пользователя "}.
{"Shared Roster", "Общие контакты"}.
{"Online", "Подключён"}.
{"Validate", "Утвердить"}.
{"Not found", "Не найдено"}.
{"Shared roster groups", "Группы общих контактов"}.
{"Not Found", "Не найдено"}.
{"Shared Roster Groups", "Группы общих контактов"}.
{"Name:", "Название:"}.
{"Description:", "Описание:"}.
{"Members:", "Члены:"}.
{"Displayed Groups:", "Видимые группы:"}.
{"Group ", "Группа "}.
{"Users last activity", "Статистика последнего подключения пользователей"}.
{"Users Last Activity", "Статистика последнего подключения пользователей"}.
{"Period: ", "Период"}.
{"Last month", "За последний месяц"}.
{"Last year", "За последний год"}.
@@ -307,9 +364,8 @@
{"Show Ordinary Table", "Показать обычную таблицу"}.
{"Show Integral Table", "Показать интегральную таблицу"}.
{"Start", "Запустить"}.
{"Modules Management", "Управление модулями"}.
{"Modules at ", "Модули на "}.
{"No data", "Нет данных"}.
{"No Data", "Нет данных"}.
{"Virtual Hosts", "Виртуальные хосты"}.
{"ejabberd virtual hosts", "Виртуальные хосты ejabberd"}.
{"Host", "Хост"}.
+136 -82
View File
@@ -9,8 +9,26 @@
{"Illegal resource format", "Неправильний формат ресурсу"}.
{"Resource conflict", "Конфлікт ресурсів"}.
% mod_adhoc.erl
{"Commands", "Команди"}.
{"Ping", "Пінг"}.
{"Pong", "Понг"}.
% mod_announce.erl
{"Really delete message of the day?", "Дійсно видалити повідомлення дня"}.
{"Subject", "Тема"}.
{"Message body", "Тіло повідомлення"}.
{"No body provided for announce message", "Тіло оголошення має бути непустим"}.
{"Announcements", "Оголошення"}.
{"Send announcement to all users", "Надіслати оголошення всім користувачам"}.
{"Send announcement to all online users", "Надіслати оголошення всім підключеним користувачам"}.
{"Send announcement to all online users on all hosts", "Надіслати оголошення всім підключеним користувачам на всіх віртуальних серверах"}.
{"Set message of the day and send to online users", "Встановити повідомлення дня та надіслати його підключеним користувачам"}.
{"Update message of the day (don't send)", "Поновити повідомлення дня (не надсилати)"}.
{"Delete message of the day", "Видалити повідомлення дня"}.
% mod_configure.erl
{"DB Tables Configuration at ", "Конфігурація таблиць БД на "}.
{"Database Tables Configuration at ", "Конфігурація таблиць бази даних на "}.
{"Choose storage type of tables", "Оберіть тип збереження таблиць"}.
{"RAM copy", "ОЗП"}.
{"RAM and disc copy", "ОЗП та диск"}.
@@ -50,7 +68,7 @@
{"Configuration", "Конфігурація"}.
{"Online Users", "Підключені користувачі"}.
{"All Users", "Всі користувачі"}.
{"Outgoing S2S connections", "Вихідні S2S-з'єднання"}.
{"Outgoing s2s Connections", "Вихідні s2s-з'єднання"}.
{"To ~s", "До ~s"}.
{"From ~s", "Від ~s"}.
{"Running Nodes", "Працюючі вузли"}.
@@ -59,12 +77,12 @@
{"Access Control Lists", "Списки керування доступом"}.
{"Access Rules", "Правила доступу"}.
{"Remove Users", "Видалення користувачів"}.
{"DB", "БД"}.
{"Database", "База даних"}.
{"Modules", "Модулі"}.
{"Start Modules", "Запуск модулів"}.
{"Stop Modules", "Зупинка модулів"}.
{"Backup Management", "Керування резервним копіюванням"}.
{"Import users from jabberd1.4 spool files", "Імпорт користувачів зі спулу jabberd1.4"}.
{"Import Users From jabberd 1.4 Spool Files", "Імпорт користувачів зі спулу jabberd 1.4"}.
{"Backup", "Резервне копіювання"}.
{"Restore", "Відновлення з резервної копії"}.
{"Dump to Text File", "Копіювання в текстовий файл"}.
@@ -76,16 +94,17 @@
"Виберіть назву користувача та пароль для реєстрації на цьому сервері"}.
% mod_vcard.erl
{"Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin",
"Erlang Jabber Server\nCopyright (c) 2002-2005 Олексій Щепін"}.
{"ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin",
"ejabberd vCard модуль\nCopyright (c) 2003-2005 Олексій Щепін"}.
{"Erlang Jabber Server\nCopyright (c) 2002-2006 Alexey Shchepin",
"Erlang Jabber Server\nCopyright (c) 2002-2006 Олексій Щепін"}.
{"ejabberd vCard module\nCopyright (c) 2003-2006 Alexey Shchepin",
"ejabberd vCard модуль\nCopyright (c) 2003-2006 Олексій Щепін"}.
{"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 ", "Результати пошуку в "}.
{"Search Results for ", "Результати пошуку в "}.
{"Jabber ID", "Jabber ID"}.
{"User", "Користувач"}.
{"Full Name", "Повне ім'я"}.
{"Name", "Ім'я"}.
@@ -95,7 +114,7 @@
{"Birthday", "День народження"}.
{"Country", "Країна"}.
{"City", "Місто"}.
{"email", "email"}.
{"Email", "Електронна пошта"}.
{"Organization Name", "Назва організації"}.
{"Organization Unit", "Відділ організації"}.
@@ -105,8 +124,8 @@
"Заповніть поля для пошуку користувача Jabber"}.
% mod_pubsub/mod_pubsub.erl
{"ejabberd pub/sub module\nCopyright (c) 2003-2005 Alexey Shchepin",
"ejabberd pub/sub модуль\nCopyright (c) 2003-2005 Олексій Щепін"}.
{"ejabberd pub/sub module\nCopyright (c) 2003-2006 Alexey Shchepin",
"ejabberd pub/sub модуль\nCopyright (c) 2003-2006 Олексій Щепін"}.
{"Node Creator", "Створювач збірника"}.
{"Deliver payloads with event notifications", "Доставляти разом з повідомленнями про публікації самі публікації"}.
{"Notify subscribers when the node configuration changes", "Повідомляти передплатників про зміни в конфігурації збірника"}.
@@ -127,60 +146,89 @@
"Для реєстрації псевдоніму необхідний x:data-придатний клієнт"}.
{"Nickname Registration at ", "Реєстрація псевдоніма на "}.
{"Enter nickname you want to register", "Введіть псевдонім, який ви хочете зареєструвати"}.
{"ejabberd MUC module\nCopyright (c) 2003-2005 Alexey Shchepin",
"ejabberd MUC модуль\nCopyright (c) 2003-2005 Алексей Щепин"}.
{"ejabberd MUC module\nCopyright (c) 2003-2006 Alexey Shchepin",
"ejabberd MUC модуль\nCopyright (c) 2003-2006 Алексей Щепин"}.
{"Only service administrators are allowed to send service messages",
"Тільки адміністратор сервісу може надсилати службові повідомлення"}.
{"Room creation is denied by service policy",
"Створювати конференцію заборонено політикою служби"}.
{"Conference room does not exist", "Конференція не існує"}.
{"Access denied by service policy", "Доступ заборонений політикою служби"}.
{"You must fill in field \"nick\" in the form",
"Вам необхідно заповнити поле \"nick\" у формі"}.
{"You must fill in field \"Nickname\" in the form",
"Вам необхідно заповнити поле \"Псевдонім\" у формі"}.
{"Specified nickname is already registered", "Вказаний псевдонім вже зареєстрований"}.
% /home/sergei/src/ejabberd/ejabberd/src/mod_muc/mod_muc_log.erl
{"Chatroom configuration modified", "Конфігурація кімнати змінилась"}.
{"joins the room", "увійшов(ла) в кімнату"}.
{"leaves the room", "вийшов(ла) з кімнати"}.
{"has been kicked", "вигнали з кімнати"}.
{"has been banned", "заборонили вхід в кімнату"}.
{"is now known as", "змінив(ла) им'я на"}.
{"Monday", "Понеділок"}.
{"Tuesday", "Вівторок"}.
{"Wednesday", "Середа"}.
{"Thursday", "Четвер"}.
{"Friday", "П'ятниця"}.
{"Saturday", "Субота"}.
{"Sunday", "Неділя"}.
{"January", "січня"}.
{"February", "лютого"}.
{"March", "березня"}.
{"April", "квітня"}.
{"May", "травня"}.
{"June", "червня"}.
{"July", "липня"}.
{"August", "серпня"}.
{"September", "вересня"}.
{"October", "грудня"}.
{"November", "листопада"}.
{"December", "грудня"}.
{"Room Configuration", "Конфігурація кімнати"}.
% mod_muc/mod_muc_room.erl
{" has set the subject to: ", " встановив(ла) тему: "}.
{"This room is not anonymous", "Ця кімната не анонімна"}.
{"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?", "Зробити кімнату захищеною паролем?"}.
{"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 participants",
"Зробити користувачів учасниками за замовчуванням"}.
{"Make room members-only",
"Кімната тільки для зареєтрованых учасників"}.
{"Allow users to send invites",
"Дозволити користувачам надсилати запрошення"}.
{"Make room password protected", "Зробити кімнату захищеною паролем"}.
{"Password", "Пароль"}.
{"Make room anonymous?", "Зробити кімнату анонімною?"}.
{"Enable logging?", "Включити журнал роботи?"}.
{"Make room semianonymous", "Зробити кімнату напіванонімною"}.
{"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\""}.
"Не дозволяється надсилати приватні повідомлення типу \"groupchat\""}.
{"Recipient is not in the conference room", "Адресата немає в конференції"}.
{"Only occupants are allowed to send queries to the conference",
"Тільки присутнім дозволяється відправляти запити в конференцію"}.
@@ -198,10 +246,12 @@
{"Invalid role: ~s", "Недопустима роль: ~s"}.
{"Owner privileges required", "Необхідні права власника"}.
{"private, ", "приватна, "}.
{"Description", "Опис"}.
{"Number of occupants", "Кількість присутніх"}.
% mod_irc/mod_irc.erl
{"ejabberd IRC module\nCopyright (c) 2003-2005 Alexey Shchepin",
"ejabberd IRC модуль\nCopyright (c) 2003-2005 Олексій Щепін"}.
{"ejabberd IRC module\nCopyright (c) 2003-2006 Alexey Shchepin",
"ejabberd IRC модуль\nCopyright (c) 2003-2006 Олексій Щепін"}.
{"You need an x:data capable client to configure mod_irc settings",
"Для налагодження параметрів mod_irc необхідний x:data-придатний клієнт"}.
{"Registration in mod_irc for ", "Реєстрація в mod_irc для "}.
@@ -214,94 +264,99 @@
% web/ejabberd_web_admin.erl
{"ejabberd Web Interface", "Web-інтерфейс ejabberd"}.
{"ejabberd administration", "Адміністрування ejabberd"}.
{"Administration", "Адміністрування"}.
{"Users", "Користувачі"}.
{"Nodes", "Вузли"}.
{"Statistics", "Статистика"}.
{"(raw)", "(необроблений формат)"}.
{"submitted", "відправлено"}.
{"bad format", "неправильний формат"}.
{"raw", "необроблений формат"}.
{"ejabberd access control lists configuration", "Конфігурація списків керування доступом ejabberd"}.
{"(Raw)", "(Необроблений формат)"}.
{"Submitted", "Відправлено"}.
{"Bad format", "Неправильний формат"}.
{"Raw", "необроблений формат"}.
{"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", "Зареєстровані користувачі"}.
{"Authenticated users", "Аутентифіковані користувачі"}.
{"Online users", "Підключені користувачі"}.
{"Outgoing S2S servers", "Вихідні S2S-сервери"}.
{"Registered Users", "Зареєстровані користувачі"}.
{"Registered Users:", "Зареєстровані користувачі:"}.
{"Authenticated Users:", "Аутентифіковані користувачі:"}.
{"Online Users", "Підключені користувачі"}.
{"Online Users:", "Підключені користувачі:"}.
{"Outgoing s2s Connections:", "Вихідні s2s-з'єднання:"}.
{"Outgoing s2s Servers:", "Вихідні s2s-сервери:"}.
{"Change Password", "Змінити пароль"}.
{"Connected Resources:", "Підключені ресурси:"}.
{"Password:", "Пароль:"}.
{"None", "Немає"}.
{"Node ", "Вузол "}.
{"DB Management", "Управління БД"}.
{"Listened Ports Management", "Управління відкритими портами"}.
{"Listened Ports", "Відкриті порти"}.
{"Restart", "Перезапустити"}.
{"Stop", "Зупинити"}.
{"RPC call error", "Помилка виклику RPC"}.
{"DB Tables at ", "Таблиці БД на "}.
{"RPC Call Error", "Помилка виклику RPC"}.
{"Database Tables at ", "Таблиці бази даних на "}.
{"Name", "Назва"}.
{"Storage Type", "Тип таблиці"}.
{"Size", "Розмір"}.
{"Memory", "Пам'ять"}.
{"Backup Management at ", "Управління резервним копіюванням на "}.
{"Store a backup in a file", "Зберегти резервну копію в файл"}.
{"Backup of ", "Резервне копіювання "}.
{"Remark that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.", "Зауважте, що тут відбувається резервне копіювання тільки вбудованної бази даних Mnesia. Якщо Ви також використовуєте інше сховище для даних (наприклад за допомогою модуля ODBC), то його резервне копіювання потрібно робити окремо."}.
{"Store binary backup:", "Зберегти бінарну резервну копію:"}.
{"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", "Відновити базу даних з текстового файла"}.
{"Restore binary backup immediately:", "Відновити з бінарної резервної копії негайно:"}.
{"Restore binary backup after next ejabberd restart (requires less memory):", "Відновити з бінарної резервної копії при наступному запуску (потребує менше пам'яті):"}.
{"Store plain text backup:", "Зберегти текстову резервну копію:"}.
{"Restore plain text backup immediately:", "Відновити з текстової резервної копії негайно:"}.
{"Listened Ports at ", "Відкриті порти на "}.
{"~p statistics", "статистика вузла ~p"}.
{"Uptime", "Час роботи сервера"}.
{"CPU Time", "Процесорний час"}.
{"Transactions commited", "Транзакції завершені"}.
{"Transactions aborted", "Транзакції відмінені"}.
{"Transactions restarted", "Транзакції перезапущені"}.
{"Transactions logged", "Транзакції запротокольовані"}.
{"Statistics of ~p", "статистика вузла ~p"}.
{"Uptime:", "Час роботи:"}.
{"CPU Time:", "Процесорний час:"}.
{"Transactions Commited:", "Транзакції завершені:"}.
{"Transactions Aborted:", "Транзакції відмінені:"}.
{"Transactions Restarted:", "Транзакції перезапущені:"}.
{"Transactions Logged:", "Транзакції запротокольовані:"}.
{"Update ", "Поновлення "}.
{"Update plan", "План поновлення"}.
{"Updated modules", "Поновлені модулі"}.
{"Update script", "Сценарій поновлення"}.
{"Low level update script", "Низькорівневий сценарій поновлення"}.
{"Script check", "Перевірка сценарію"}.
{"Port", "Порт"}.
{"Module", "Модуль"}.
{"Options", "Параметри"}.
{"Update", "Обновити"}.
{"Delete", "Видалити"}.
{"Add User", "Додати користувача"}.
{"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One",
"ejabberd (c) 2002-2005 Олексій Щепін, 2004-2005 Process One"}.
{"Offline messages", "Офлайнові повідомлення"}.
{"ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One",
"ejabberd (c) 2002-2006 Олексій Щепін, 2004-2006 Process One"}.
{"Offline Messages", "Офлайнові повідомлення"}.
{"Offline Messages:", "Офлайнові повідомлення:"}.
{"Last Activity", "Останнє підключення"}.
{"Never", "Ніколи"}.
{"~s offline messages queue", "Черга офлайнових повідомлень ~s"}.
{"~s's Offline Messages Queue", "Черга офлайнових повідомлень ~s"}.
{"Time", "Час"}.
{"From", "Від кого"}.
{"To", "Кому"}.
{"Packet", "Пакет"}.
{"Offline messages:", "Офлайнові повідомлення:"}.
{"Roster", "Ростер"}.
{"Nickname", "Псевдонім"}.
{"Subscription", "Підписка"}.
{"Pending", "Очікування"}.
{"Groups", "Групи"}.
{"Remove", "Видалити"}.
{"Add JID", "Додати JID"}.
{"Add Jabber ID", "Додати Jabber ID"}.
{"User ", "Користувач "}.
{"Roster of ", "Ростер користувача "}.
{"Shared Roster", "Спільні контакти"}.
{"Online", "Підключений"}.
{"Validate", "Затвердити"}.
{"Not found", "Не знайдено"}.
{"Shared roster groups", "Спільні групи контактів"}.
{"Not Found", "Не знайдено"}.
{"Shared Roster Groups", "Спільні групи контактів"}.
{"Name:", "Назва:"}.
{"Description:", "Опис:"}.
{"Members:", "Члени:"}.
{"Displayed Groups:", "Видимі групи:"}.
{"Group ", "Група "}.
{"Users last activity", "Статистика останнього підключення користувачів"}.
{"Users Last Activity", "Статистика останнього підключення користувачів"}.
{"Period: ", "Період"}.
{"Last month", "За останній місяць"}.
{"Last year", "За останній рік"}.
@@ -309,9 +364,8 @@
{"Show Ordinary Table", "Показати звичайну таблицю"}.
{"Show Integral Table", "Показати інтегральну таблицю"}.
{"Start", "Запустити"}.
{"Modules Management", "Керування модулями"}.
{"Modules at ", "Модулі на "}.
{"No data", "Немає даних"}.
{"No Data", "Немає даних"}.
{"Virtual Hosts", "Віртуальні хости"}.
{"ejabberd virtual hosts", "віртуальні хости ejabberd"}.
{"Host", "Хост"}.
+121 -27
View File
@@ -17,7 +17,8 @@
sql_query/2,
sql_query_t/1,
sql_transaction/2,
escape/1]).
escape/1,
escape_like/1]).
%% gen_server callbacks
-export([init/1,
@@ -27,10 +28,13 @@
handle_info/2,
terminate/2]).
-include("ejabberd.hrl").
-record(state, {db_ref, db_type}).
-define(STATE_KEY, ejabberd_odbc_state).
-define(MAX_TRANSACTION_RESTARTS, 10).
-define(MYSQL_PORT, 3306).
%%%----------------------------------------------------------------------
%%% API
@@ -45,10 +49,22 @@ sql_query(Host, Query) ->
gen_server:call(ejabberd_odbc_sup:get_random_pid(Host),
{sql_query, Query}, 60000).
%% SQL transaction based on a list of queries
%% This function automatically
sql_transaction(Host, Queries) when is_list(Queries) ->
F = fun() ->
lists:foreach(fun(Query) ->
sql_query_t(Query)
end,
Queries)
end,
sql_transaction(Host, F);
%% SQL transaction, based on a erlang anonymous function (F = fun)
sql_transaction(Host, F) ->
gen_server:call(ejabberd_odbc_sup:get_random_pid(Host),
{sql_transaction, F}, 60000).
%% This function is intended to be used from inside an sql_transaction:
sql_query_t(Query) ->
State = get(?STATE_KEY),
QRes = sql_query_internal(State, Query),
@@ -69,20 +85,27 @@ sql_query_t(Query) ->
QRes
end.
escape(S) ->
[case C of
$\0 -> "\\0";
$\n -> "\\n";
$\t -> "\\t";
$\b -> "\\b";
$\r -> "\\r";
$' -> "\\'";
$" -> "\\\"";
$% -> "\\%";
$_ -> "\\_";
$\\ -> "\\\\";
_ -> C
end || C <- S].
%% Escape character that will confuse an SQL engine
escape(S) when is_list(S) ->
[escape(C) || C <- S];
escape($\0) -> "\\0";
escape($\n) -> "\\n";
escape($\t) -> "\\t";
escape($\b) -> "\\b";
escape($\r) -> "\\r";
escape($') -> "\\'";
escape($") -> "\\\"";
escape($\\) -> "\\\\";
escape(C) -> C.
%% Escape character that will confuse an SQL engine
%% Percent and underscore only need to be escaped for pattern matching like
%% statement
escape_like(S) when is_list(S) ->
[escape_like(C) || C <- S];
escape_like($%) -> "\\%";
escape_like($_) -> "\\_";
escape_like(C) -> escape(C).
%%%----------------------------------------------------------------------
@@ -100,16 +123,14 @@ init([Host]) ->
SQLServer = ejabberd_config:get_local_option({odbc_server, Host}),
case SQLServer of
{pgsql, Server, DB, Username, Password} ->
{ok, Ref} = pgsql:connect(Server, DB, Username, Password),
{ok, #state{db_ref = Ref,
db_type = pgsql}};
pgsql_connect(Server, DB, Username, Password);
{mysql, Server, DB, Username, Password} ->
mysql_connect(Server, DB, Username, Password);
_ when is_list(SQLServer) ->
{ok, Ref} = odbc:connect(SQLServer,
[{scrollable_cursors, off}]),
{ok, #state{db_ref = Ref,
db_type = odbc}}
odbc_connect(SQLServer)
end.
%%----------------------------------------------------------------------
%% Func: handle_call/3
%% Returns: {reply, Reply, State} |
@@ -150,6 +171,11 @@ code_change(_OldVsn, State, _Extra) ->
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%%----------------------------------------------------------------------
%% We receive the down signal when we loose the MySQL connection (we are
%% monitoring the connection)
%% => We exit and let the supervisor restart the connection.
handle_info({'DOWN', _MonitorRef, process, _Pid, _Info}, State) ->
{stop, connection_dropped, State};
handle_info(_Info, State) ->
{noreply, State}.
@@ -164,31 +190,66 @@ terminate(_Reason, _State) ->
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
sql_query_internal(State, Query) ->
case State#state.db_type of
odbc ->
odbc:sql_query(State#state.db_ref, Query);
pgsql ->
pgsql_to_odbc(pgsql:squery(State#state.db_ref, Query))
pgsql_to_odbc(pgsql:squery(State#state.db_ref, Query));
mysql ->
mysql_to_odbc(mysql_conn:fetch(State#state.db_ref, Query, self()))
end.
execute_transaction(_State, _F, 0) ->
{aborted, restarts_exceeded};
execute_transaction(State, F, NRestarts) ->
put(?STATE_KEY, State),
sql_query_internal(State, "begin"),
sql_query_internal(State, "begin;"),
case catch F() of
aborted ->
execute_transaction(State, F, NRestarts - 1);
{'EXIT', Reason} ->
sql_query_internal(State, "rollback"),
sql_query_internal(State, "rollback;"),
{aborted, Reason};
Res ->
sql_query_internal(State, "commit"),
sql_query_internal(State, "commit;"),
{atomic, Res}
end.
%% == pure ODBC code
%% part of init/1
%% Open an ODBC database connection
odbc_connect(SQLServer) ->
case odbc:connect(SQLServer,[{scrollable_cursors, off}]) of
{ok, Ref} ->
erlang:monitor(process, Ref),
{ok, #state{db_ref = Ref, db_type = odbc}};
{error, Reason} ->
?ERROR_MSG("ODBC connection (~s) failed: ~p~n",
[SQLServer, Reason]),
%% If we can't connect we wait for 30 seconds before retrying
timer:sleep(30000),
{stop, odbc_connection_failed}
end.
%% == Native PostgreSQL code
%% part of init/1
%% Open a database connection to PostgreSQL
pgsql_connect(Server, DB, Username, Password) ->
case pgsql:connect(Server, DB, Username, Password) of
{ok, Ref} ->
{ok, #state{db_ref = Ref, db_type = pgsql}};
{error, Reason} ->
?ERROR_MSG("PostgreSQL connection failed: ~p~n", [Reason]),
%% If we can't connect we wait for 30 seconds before retrying
timer:sleep(30000),
{stop, pgsql_connection_failed}
end.
%% Convert PostgreSQL query result to Erlang ODBC result formalism
pgsql_to_odbc({ok, PGSQLResult}) ->
case PGSQLResult of
[Item] ->
@@ -211,3 +272,36 @@ pgsql_item_to_odbc({error, Error}) ->
pgsql_item_to_odbc(_) ->
{updated,undefined}.
%% == Native MySQL code
%% part of init/1
%% Open a database connection to MySQL
mysql_connect(Server, DB, Username, Password) ->
NoLogFun = fun(_Level,_Format,_Argument) -> ok end,
case mysql_conn:start(Server, ?MYSQL_PORT, Username, Password, DB, NoLogFun) of
{ok, Ref} ->
erlang:monitor(process, Ref),
{ok, #state{db_ref = Ref, db_type = mysql}};
{error, Reason} ->
?ERROR_MSG("MySQL connection failed: ~p~n", [Reason]),
%% If we can't connect we wait for 30 seconds before retrying
timer:sleep(30000),
{stop, mysql_connection_failed}
end.
%% Convert MySQL query result to Erlang ODBC result formalism
mysql_to_odbc({updated, MySQLRes}) ->
{updated, mysql:get_result_affected_rows(MySQLRes)};
mysql_to_odbc({data, MySQLRes}) ->
mysql_item_to_odbc(mysql:get_result_field_info(MySQLRes),
mysql:get_result_rows(MySQLRes));
mysql_to_odbc({error, MySQLRes}) ->
{error, mysql:get_result_reason(MySQLRes)}.
%% When tabular data is returned, convert it to the ODBC formalism
mysql_item_to_odbc(Columns, Recs) ->
%% For now, there is a bug and we do not get the correct value from MySQL
%% module:
{selected,
[element(2, Column) || Column <- Columns],
[list_to_tuple(Rec) || Rec <- Recs]}.
+1 -1
View File
@@ -25,7 +25,7 @@ start_link(Host) ->
init([Host]) ->
% TODO
N = 10,
{ok, {{one_for_one, 10, 1},
{ok, {{one_for_one, 10, 6},
lists:map(
fun(I) ->
{I,
+93
View File
@@ -0,0 +1,93 @@
-- Needs MySQL max with innodb back-end
CREATE TABLE users (
username varchar(250) PRIMARY KEY,
password text NOT NULL
) TYPE=InnoDB CHARACTER SET utf8;
CREATE TABLE last (
username varchar(250) PRIMARY KEY,
seconds text NOT NULL,
state text
) TYPE=InnoDB CHARACTER SET utf8;
CREATE TABLE rosterusers (
username varchar(250) NOT NULL,
jid varchar(250) NOT NULL,
nick text,
subscription character(1) NOT NULL,
ask character(1) NOT NULL,
server character(1) NOT NULL,
subscribe text,
type text
) TYPE=InnoDB CHARACTER SET utf8;
CREATE UNIQUE INDEX i_rosteru_user_jid USING HASH ON rosterusers(username(75), jid(75));
CREATE INDEX i_rosteru_username USING HASH ON rosterusers(username);
CREATE INDEX i_rosteru_jid USING HASH ON rosterusers(jid);
CREATE TABLE rostergroups (
username varchar(250) NOT NULL,
jid varchar(250) NOT NULL,
grp text NOT NULL
) TYPE=InnoDB CHARACTER SET utf8;
CREATE INDEX pk_rosterg_user_jid USING HASH ON rostergroups(username(75), jid(75));
CREATE TABLE spool (
username varchar(250) NOT NULL,
xml text,
seq SERIAL
) TYPE=InnoDB CHARACTER SET utf8;
CREATE INDEX i_despool USING BTREE ON spool(username);
CREATE TABLE vcard (
username varchar(250) PRIMARY KEY,
vcard text NOT NULL
) TYPE=InnoDB CHARACTER SET utf8;
CREATE TABLE vcard_search (
username varchar(250) NOT NULL,
lusername varchar(250) PRIMARY KEY,
fn text NOT NULL,
lfn varchar(250) NOT NULL,
family text NOT NULL,
lfamily varchar(250) NOT NULL,
given text NOT NULL,
lgiven varchar(250) NOT NULL,
middle text NOT NULL,
lmiddle varchar(250) NOT NULL,
nickname text NOT NULL,
lnickname varchar(250) NOT NULL,
bday text NOT NULL,
lbday varchar(250) NOT NULL,
ctry text NOT NULL,
lctry varchar(250) NOT NULL,
locality text NOT NULL,
llocality varchar(250) NOT NULL,
email text NOT NULL,
lemail varchar(250) NOT NULL,
orgname text NOT NULL,
lorgname varchar(250) NOT NULL,
orgunit text NOT NULL,
lorgunit varchar(250) NOT NULL
) TYPE=InnoDB CHARACTER SET utf8;
CREATE INDEX i_vcard_search_lfn ON vcard_search(lfn);
CREATE INDEX i_vcard_search_lfamily ON vcard_search(lfamily);
CREATE INDEX i_vcard_search_lgiven ON vcard_search(lgiven);
CREATE INDEX i_vcard_search_lmiddle ON vcard_search(lmiddle);
CREATE INDEX i_vcard_search_lnickname ON vcard_search(lnickname);
CREATE INDEX i_vcard_search_lbday ON vcard_search(lbday);
CREATE INDEX i_vcard_search_lctry ON vcard_search(lctry);
CREATE INDEX i_vcard_search_llocality ON vcard_search(llocality);
CREATE INDEX i_vcard_search_lemail ON vcard_search(lemail);
CREATE INDEX i_vcard_search_lorgname ON vcard_search(lorgname);
CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit);
+6 -6
View File
@@ -36,12 +36,12 @@ CREATE TABLE rostergroups (
CREATE INDEX pk_rosterg_user_jid ON rostergroups USING btree (username, jid);
;; To update from previous table definition:
; CREATE SEQUENCE spool_seq_seq;
; ALTER TABLE spool ADD COLUMN seq integer;
; ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq');
; UPDATE spool SET seq = DEFAULT;
; ALTER TABLE spool ALTER COLUMN seq SET NOT NULL;
--- To update from previous table definition:
-- CREATE SEQUENCE spool_seq_seq;
-- ALTER TABLE spool ADD COLUMN seq integer;
-- ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq');
-- UPDATE spool SET seq = DEFAULT;
-- ALTER TABLE spool ALTER COLUMN seq SET NOT NULL;
CREATE TABLE spool (
username text NOT NULL,
+14 -13
View File
@@ -33,25 +33,26 @@ new1({maxrate, MaxRate}) ->
lasttime = now_to_usec(now())}.
update(none, Size) ->
none;
update(none, _Size) ->
{none, 0};
update(#maxrate{} = State, Size) ->
MinInterv = 1000 * Size /
(2 * State#maxrate.maxrate - State#maxrate.lastrate),
Interv = (now_to_usec(now()) - State#maxrate.lasttime) / 1000,
%io:format("State: ~p, Size=~p~nM=~p, I=~p~n",
% [State, Size, MinInterv, Interv]),
if
MinInterv > Interv ->
timer:sleep(1 + trunc(MinInterv - Interv));
true ->
ok
end,
Now = now_to_usec(now()),
State#maxrate{
lastrate = (State#maxrate.lastrate +
1000000 * Size / (Now - State#maxrate.lasttime))/2,
lasttime = Now}.
Pause = if
MinInterv > Interv ->
1 + trunc(MinInterv - Interv);
true ->
0
end,
NextNow = now_to_usec(now()) + Pause * 1000,
{State#maxrate{
lastrate = (State#maxrate.lastrate +
1000000 * Size / (NextNow - State#maxrate.lasttime))/2,
lasttime = NextNow},
Pause}.
now_to_usec({MSec, Sec, USec}) ->
+8
View File
@@ -16,6 +16,8 @@
tcp_to_tls/2, tls_to_tcp/1,
send/2,
recv/2, recv/3, recv_data/2,
setopts/2,
controlling_process/2,
close/1,
get_peer_certificate/1,
get_verify_result/1,
@@ -175,6 +177,12 @@ send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
end.
setopts(#tlssock{tcpsock = TCPSocket}, Opts) ->
inet:setopts(TCPSocket, Opts).
controlling_process(#tlssock{tcpsock = TCPSocket}, Pid) ->
gen_tcp:controlling_process(TCPSocket, Pid).
close(#tlssock{tcpsock = TCPSocket, tlsport = Port}) ->
gen_tcp:close(TCPSocket),
port_close(Port).
+1 -1
View File
@@ -19,7 +19,7 @@ OBJS = \
all: $(OBJS)
$(OUTDIR)/%.beam: %.erl
$(OUTDIR)/%.beam: %.erl ejabberd_http.hrl
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
+68 -116
View File
@@ -8,11 +8,12 @@
-module(ejabberd_http).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-vsn('$Revision$ ').
%% External exports
-export([start/2,
start_link/2,
become_controller/1,
receive_headers/1,
url_encode/1]).
@@ -81,6 +82,9 @@ start_link({SockMod, Socket}, Opts) ->
use_web_admin = UseWebAdmin}])}.
become_controller(_Pid) ->
ok.
send_text(State, Text) ->
(State#state.sockmod):send(State#state.socket, Text).
@@ -197,60 +201,34 @@ process_request(#state{request_method = 'GET',
request_lang = Lang,
use_http_poll = UseHTTPPoll,
use_web_admin = UseWebAdmin} = State) ->
US = case Auth of
{SJID, P} ->
case jlib:string_to_jid(SJID) of
error ->
unauthorized;
#jid{user = U, server = S} ->
case ejabberd_auth:check_password(U, S, P) of
true ->
{U, S};
false ->
unauthorized
end
end;
_ ->
undefined
end,
case US of
unauthorized ->
make_xhtml_output(
State,
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,
us = US,
lang = Lang},
case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
Request) of
El when element(1, El) == xmlelement ->
make_xhtml_output(State, 200, [], El);
{Status, Headers, El} when
element(1, El) == xmlelement ->
make_xhtml_output(State, Status, Headers, El);
Text when is_list(Text) ->
make_text_output(State, 200, [], Text);
{Status, Headers, Text} when
is_list(Text) ->
make_text_output(State, Status, Headers, Text)
end
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,
auth = Auth,
lang = Lang},
case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
Request) of
El when element(1, El) == xmlelement ->
make_xhtml_output(State, 200, [], El);
{Status, Headers, El} when
element(1, El) == xmlelement ->
make_xhtml_output(State, Status, Headers, El);
Text when is_list(Text) ->
make_text_output(State, 200, [], Text);
{Status, Headers, Text} when
is_list(Text) ->
make_text_output(State, Status, Headers, Text)
end
end;
@@ -264,68 +242,42 @@ process_request(#state{request_method = 'POST',
use_http_poll = UseHTTPPoll,
use_web_admin = UseWebAdmin} = State)
when is_integer(Len) ->
US = case Auth of
{SJID, P} ->
case jlib:string_to_jid(SJID) of
error ->
unauthorized;
#jid{user = U, server = S} ->
case ejabberd_auth:check_password(U, S, P) of
true ->
{U, S};
false ->
unauthorized
end
end;
_ ->
undefined
end,
case US of
unauthorized ->
make_xhtml_output(
State,
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}]);
_ ->
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,
us = US,
data = Data,
lang = Lang},
case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
Request) of
El when element(1, El) == xmlelement ->
make_xhtml_output(State, 200, [], El);
{Status, Headers, El} when
element(1, El) == xmlelement ->
make_xhtml_output(State, Status, Headers, El);
Text when is_list(Text) ->
make_text_output(State, 200, [], Text);
{Status, Headers, Text} when is_list(Text) ->
make_text_output(State, Status, Headers, Text)
end
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,
auth = Auth,
data = Data,
lang = Lang},
case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
Request) of
El when element(1, El) == xmlelement ->
make_xhtml_output(State, 200, [], El);
{Status, Headers, El} when
element(1, El) == xmlelement ->
make_xhtml_output(State, Status, Headers, El);
Text when is_list(Text) ->
make_text_output(State, 200, [], Text);
{Status, Headers, Text} when is_list(Text) ->
make_text_output(State, Status, Headers, Text)
end
end;
+1
View File
@@ -10,6 +10,7 @@
path,
q = [],
us,
auth,
lang = "",
data = ""
}).
+22 -11
View File
@@ -21,7 +21,8 @@
handle_info/3,
terminate/3,
send/2,
recv/3,
setopts/2,
controlling_process/2,
close/1,
process_request/1]).
@@ -66,8 +67,16 @@ start_link(ID, Key) ->
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).
setopts({http_poll, FsmRef}, Opts) ->
case lists:member({active, once}, Opts) of
true ->
gen_fsm:sync_send_all_state_event(FsmRef, activate);
_ ->
ok
end.
controlling_process(_Socket, _Pid) ->
ok.
close({http_poll, FsmRef}) ->
catch gen_fsm:sync_send_all_state_event(FsmRef, close).
@@ -138,7 +147,8 @@ process_request(_Request) ->
init([ID, Key]) ->
?INFO_MSG("started: ~p", [{ID, Key}]),
Opts = [], % TODO
ejabberd_c2s:start({?MODULE, {http_poll, self()}}, Opts),
{ok, C2SPid} = ejabberd_c2s:start({?MODULE, {http_poll, self()}}, Opts),
ejabberd_c2s:become_controller(C2SPid),
Timer = erlang:start_timer(?HTTP_POLL_TIMEOUT, self(), []),
{ok, loop, #state{id = ID,
key = Key,
@@ -188,14 +198,14 @@ handle_sync_event({send, Packet}, From, StateName, StateData) ->
Reply = ok,
{reply, Reply, StateName, StateData#state{output = Output}};
handle_sync_event(recv, From, StateName, StateData) ->
handle_sync_event(activate, From, StateName, StateData) ->
case StateData#state.input of
"" ->
{next_state, StateName, StateData#state{waiting_input = From}};
{reply, ok, StateName, StateData#state{waiting_input = From}};
Input ->
Reply = {ok, list_to_binary(Input)},
{reply, Reply, StateName, StateData#state{input = "",
waiting_input = false}}
From ! {tcp, {http_poll, self()}, list_to_binary(Input)},
{reply, ok, StateName, StateData#state{input = "",
waiting_input = false}}
end;
handle_sync_event(stop, From, StateName, StateData) ->
@@ -225,8 +235,9 @@ handle_sync_event({http_put, Key, NewKey, Packet},
Reply = ok,
{reply, Reply, StateName, StateData#state{input = Input,
key = NewKey}};
Receiver ->
gen_fsm:reply(Receiver, {ok, list_to_binary(Packet)}),
{Receiver, _Tag} ->
Receiver ! {tcp, {http_poll, self()},
list_to_binary(Packet)},
cancel_timer(StateData#state.timer),
Timer = erlang:start_timer(?HTTP_POLL_TIMEOUT, self(), []),
Reply = ok,
+42 -9
View File
@@ -8,7 +8,7 @@
-module(ejabberd_web).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-vsn('$Revision$ ').
%% External exports
-export([make_xhtml/1,
@@ -50,13 +50,29 @@ make_xhtml(Els) ->
process_get({_, true},
#request{us = US,
#request{auth = Auth,
path = ["admin", "server", SHost | RPath],
q = Query,
lang = Lang} = Request) ->
Host = jlib:nameprep(SHost),
case lists:member(Host, ?MYHOSTS) of
true ->
US = case Auth of
{SJID, P} ->
case jlib:string_to_jid(SJID) of
error ->
unauthorized;
#jid{user = U, server = S} ->
case ejabberd_auth:check_password(U, S, P) of
true ->
{U, S};
false ->
unauthorized
end
end;
_ ->
unauthorized
end,
case US of
{User, Server} ->
case acl:match_rule(
@@ -65,9 +81,10 @@ process_get({_, true},
{401, [], make_xhtml([?XC("h1", "Not Allowed")])};
allow ->
ejabberd_web_admin:process_admin(
Host, Request#request{path = RPath})
Host, Request#request{path = RPath,
us = US})
end;
undefined ->
unauthorized ->
{401,
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
@@ -78,10 +95,26 @@ process_get({_, true},
end;
process_get({_, true},
#request{us = US,
#request{auth = Auth,
path = ["admin" | RPath],
q = Query,
lang = Lang} = Request) ->
US = case Auth of
{SJID, P} ->
case jlib:string_to_jid(SJID) of
error ->
unauthorized;
#jid{user = U, server = S} ->
case ejabberd_auth:check_password(U, S, P) of
true ->
{U, S};
false ->
unauthorized
end
end;
_ ->
unauthorized
end,
case US of
{User, Server} ->
case acl:match_rule(
@@ -90,9 +123,10 @@ process_get({_, true},
{401, [], make_xhtml([?XC("h1", "Not Allowed")])};
allow ->
ejabberd_web_admin:process_admin(
global, Request#request{path = RPath})
global, Request#request{path = RPath,
us = US})
end;
undefined ->
unauthorized ->
{401,
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
@@ -100,8 +134,7 @@ process_get({_, true},
end;
process_get({true, _},
#request{us = _US,
path = ["http-poll" | RPath],
#request{path = ["http-poll" | RPath],
q = _Query,
lang = _Lang} = Request) ->
ejabberd_http_poll:process_request(Request#request{path = RPath});
+141 -91
View File
@@ -72,7 +72,7 @@ make_xhtml(Els, global, Lang) ->
[?XAE("div",
[{"id", "header"}],
[?XE("h1",
[?ACT("/admin/", "ejabberd administration")]
[?ACT("/admin/", "Administration")]
)]),
?XAE("div",
[{"id", "navigation"}],
@@ -95,7 +95,7 @@ make_xhtml(Els, global, Lang) ->
[?XAE("div",
[{"id", "copyright"}],
[?XCT("p",
"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One")
"ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One")
])])])
]}};
@@ -118,7 +118,7 @@ make_xhtml(Els, Host, Lang) ->
[?XAE("div",
[{"id", "header"}],
[?XE("h1",
[?ACT(Base, "ejabberd administration")]
[?ACT(Base, "Administration")]
)]),
?XAE("div",
[{"id", "navigation"}],
@@ -150,7 +150,7 @@ make_xhtml(Els, Host, Lang) ->
[?XAE("div",
[{"id", "copyright"}],
[?XCT("p",
"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One")
"ejabberd (c) 2002-2006 Alexey Shchepin, 2004-2006 Process One")
])])])
]}}.
@@ -527,12 +527,12 @@ process_admin(global,
path = [],
q = Query,
lang = Lang} = Request) ->
make_xhtml([?XCT("h1", "ejabberd administration"),
make_xhtml([?XCT("h1", "Administration"),
?XE("ul",
[?LI([?ACT("/admin/acls/", "Access Control Lists"), ?C(" "),
?ACT("/admin/acls-raw/", "(raw)")]),
?ACT("/admin/acls-raw/", "(Raw)")]),
?LI([?ACT("/admin/access/", "Access Rules"), ?C(" "),
?ACT("/admin/access-raw/", "(raw)")]),
?ACT("/admin/access-raw/", "(Raw)")]),
?LI([?ACT("/admin/vhosts/", "Virtual Hosts")]),
?LI([?ACT("/admin/nodes/", "Nodes")]),
?LI([?ACT("/admin/stats/", "Statistics")])
@@ -546,12 +546,12 @@ process_admin(Host,
q = Query,
lang = Lang} = Request) ->
Base = "/admin/server/" ++ Host ++ "/",
make_xhtml([?XCT("h1", "ejabberd administration"),
make_xhtml([?XCT("h1", "Administration"),
?XE("ul",
[?LI([?ACT(Base ++ "acls/", "Access Control Lists"), ?C(" "),
?ACT(Base ++ "acls-raw/", "(raw)")]),
?ACT(Base ++ "acls-raw/", "(Raw)")]),
?LI([?ACT(Base ++ "access/", "Access Rules"), ?C(" "),
?ACT(Base ++ "access-raw/", "(raw)")]),
?ACT(Base ++ "access-raw/", "(Raw)")]),
?LI([?ACT(Base ++ "users/", "Users")]),
?LI([?ACT(Base ++ "online-users/", "Online Users")]),
?LI([?ACT(Base ++ "last-activity/", "Last Activity")]),
@@ -620,10 +620,10 @@ process_admin(Host,
"~p.", [lists:keysort(
2, ets:select(acl, [{{acl, {'$1', Host}, '$2'},
[], [{{acl, '$1', '$2'}}]}]))])),
make_xhtml([?XCT("h1", "ejabberd access control lists configuration")] ++
make_xhtml([?XCT("h1", "Access Control Lists")] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
@@ -664,13 +664,13 @@ process_admin(Host,
ACLs = lists:keysort(
2, ets:select(acl, [{{acl, {'$1', Host}, '$2'},
[], [{{acl, '$1', '$2'}}]}])),
make_xhtml([?XCT("h1", "ejabberd access control lists configuration")] ++
make_xhtml([?XCT("h1", "Access Control Lists")] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XE("p", [?ACT("../acls-raw/", "raw")])] ++
[?XE("p", [?ACT("../acls-raw/", "Raw")])] ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
[acls_to_xhtml(ACLs),
?BR,
@@ -733,10 +733,10 @@ process_admin(Host,
[{{config, {access, '$1', Host}, '$2'},
[],
[{{access, '$1', '$2'}}]}])])),
make_xhtml([?XCT("h1", "ejabberd access rules configuration")] ++
make_xhtml([?XCT("h1", "Access Rules")] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
@@ -772,13 +772,13 @@ process_admin(Host,
[{{config, {access, '$1', Host}, '$2'},
[],
[{{access, '$1', '$2'}}]}]),
make_xhtml([?XCT("h1", "ejabberd access rules configuration")] ++
make_xhtml([?XCT("h1", "Access Rules")] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XE("p", [?ACT("../access-raw/", "raw")])] ++
[?XE("p", [?ACT("../access-raw/", "Raw")])] ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
[access_rules_to_xhtml(AccessRules, Lang),
?BR,
@@ -816,8 +816,8 @@ process_admin(Host,
make_xhtml([?XC("h1",
io_lib:format(?T("~s access rule configuration"), [SName]))] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
@@ -841,7 +841,7 @@ process_admin(Host,
q = Query,
lang = Lang} = Request) when is_list(Host) ->
Res = list_users(Host, Query, Lang, fun url_func/1),
make_xhtml([?XCT("h1", "ejabberd users")] ++ Res, Host, Lang);
make_xhtml([?XCT("h1", "Users")] ++ Res, Host, Lang);
process_admin(Host,
#request{us = US,
@@ -849,7 +849,7 @@ process_admin(Host,
q = Query,
lang = Lang} = Request) when is_list(Host) ->
Res = list_users_in_diapason(Host, Diap, Lang, fun url_func/1),
make_xhtml([?XCT("h1", "ejabberd users")] ++ Res, Host, Lang);
make_xhtml([?XCT("h1", "Users")] ++ Res, Host, Lang);
process_admin(Host,
#request{us = US,
@@ -857,7 +857,7 @@ process_admin(Host,
q = Query,
lang = Lang} = Request) when is_list(Host) ->
Res = list_online_users(Host, Lang),
make_xhtml([?XCT("h1", "ejabberd users")] ++ Res, Host, Lang);
make_xhtml([?XCT("h1", "Online Users")] ++ Res, Host, Lang);
process_admin(Host,
#request{method = Method,
@@ -878,7 +878,7 @@ process_admin(Host,
_ ->
list_last_activity(Host, Lang, true, Month)
end,
make_xhtml([?XCT("h1", "Users last activity")] ++
make_xhtml([?XCT("h1", "Users Last Activity")] ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
[?CT("Period: "),
?XAE("select", [{"name", "period"}],
@@ -906,7 +906,7 @@ process_admin(Host,
q = Query,
lang = Lang} = Request) ->
Res = get_stats(Host, Lang),
make_xhtml([?XCT("h1", "ejabberd stats")] ++ Res, Host, Lang);
make_xhtml([?XCT("h1", "Statistics")] ++ Res, Host, Lang);
process_admin(Host,
#request{us = US,
@@ -971,7 +971,7 @@ process_admin(Host,
process_admin(Host,
#request{lang = Lang}) ->
setelement(1, make_xhtml([?XC("h1", "Not found")], Host, Lang), 404).
setelement(1, make_xhtml([?XC("h1", "Not Found")], Host, Lang), 404).
@@ -1209,8 +1209,8 @@ list_vhosts(Lang) ->
[?XE("thead",
[?XE("tr",
[?XCT("td", "Host"),
?XCT("td", "Registered users"),
?XCT("td", "Online users")
?XCT("td", "Registered Users"),
?XCT("td", "Online Users")
])]),
?XE("tbody",
lists:map(
@@ -1254,8 +1254,8 @@ list_users(Host, Query, Lang, URLFunc) ->
end, lists:seq(1, N, M))
end,
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
@@ -1313,7 +1313,7 @@ list_given_users(Users, Prefix, Lang, URLFunc) ->
[?XE("thead",
[?XE("tr",
[?XCT("td", "User"),
?XCT("td", "Offline messages"),
?XCT("td", "Offline Messages"),
?XCT("td", "Last Activity")])]),
?XE("tbody",
lists:map(
@@ -1371,15 +1371,15 @@ get_stats(global, Lang) ->
S2SServers = length(lists:usort([element(2, C) || C <- S2SConns])),
[?XAE("table", [],
[?XE("tbody",
[?XE("tr", [?XCT("td", "Registered users"),
[?XE("tr", [?XCT("td", "Registered Users:"),
?XC("td", integer_to_list(RegisteredUsers))]),
?XE("tr", [?XCT("td", "Authenticated users"),
?XE("tr", [?XCT("td", "Authenticated Users:"),
?XC("td", integer_to_list(AuthUsers))]),
?XE("tr", [?XCT("td", "Online users"),
?XE("tr", [?XCT("td", "Online Users:"),
?XC("td", integer_to_list(OnlineUsers))]),
?XE("tr", [?XCT("td", "Outgoing S2S connections"),
?XE("tr", [?XCT("td", "Outgoing s2s Connections:"),
?XC("td", integer_to_list(S2SConnections))]),
?XE("tr", [?XCT("td", "Outgoing S2S servers"),
?XE("tr", [?XCT("td", "Outgoing s2s Servers:"),
?XC("td", integer_to_list(S2SServers))])
])
])];
@@ -1389,9 +1389,9 @@ get_stats(Host, Lang) ->
RegisteredUsers = length(ejabberd_auth:get_vh_registered_users(Host)),
[?XAE("table", [],
[?XE("tbody",
[?XE("tr", [?XCT("td", "Registered users"),
[?XE("tr", [?XCT("td", "Registered Users:"),
?XC("td", integer_to_list(RegisteredUsers))]),
?XE("tr", [?XCT("td", "Online users"),
?XE("tr", [?XCT("td", "Online Users:"),
?XC("td", integer_to_list(OnlineUsers))])
])
])].
@@ -1429,14 +1429,14 @@ user_info(User, Server, Query, Lang) ->
integer_to_list(QueueLen))],
[?XC("h1", ?T("User ") ++ us_to_list(US))] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
[?XCT("h3", "Connected Resources:")] ++ FResources ++
[?XCT("h3", "Password:")] ++ FPassword ++
[?XCT("h3", "Offline messages:")] ++ FQueueLen ++
[?XCT("h3", "Offline Messages:")] ++ FQueueLen ++
[?XE("h3", [?ACT("roster/", "Roster")])] ++
[?BR, ?INPUTT("submit", "removeuser", "Remove User")])].
@@ -1493,11 +1493,11 @@ user_queue(User, Server, Query, Lang) ->
?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
)
end, Msgs),
[?XC("h1", io_lib:format(?T("~s offline messages queue"),
[?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
[us_to_list(US)]))] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
@@ -1578,7 +1578,7 @@ user_roster(User, Server, Query, Lang, Admin) ->
[?XE("table",
[?XE("thead",
[?XE("tr",
[?XCT("td", "JID"),
[?XCT("td", "Jabber ID"),
?XCT("td", "Nickname"),
?XCT("td", "Subscription"),
?XCT("td", "Pending"),
@@ -1622,15 +1622,15 @@ user_roster(User, Server, Query, Lang, Admin) ->
end,
[?XC("h1", ?T("Roster of ") ++ us_to_list(US))] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
FItems ++
[?P,
?INPUT("text", "newjid", ""), ?C(" "),
?INPUTT("submit", "addjid", "Add JID")
?INPUTT("submit", "addjid", "Add Jabber ID")
])].
user_roster_parse_query(User, Server, Items, Query, Admin) ->
@@ -1734,7 +1734,7 @@ list_last_activity(Host, Lang, Integral, Period) ->
Hist = histogram(Vals, Integral),
if
Hist == [] ->
[?CT("No data")];
[?CT("No Data")];
true ->
Left = if
Days == infinity ->
@@ -1833,15 +1833,16 @@ get_node(global, Node, [], Query, Lang) ->
Res = node_parse_query(Node, Query),
[?XC("h1", ?T("Node ") ++ atom_to_list(Node))] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XE("ul",
[?LI([?ACT("db/", "DB Management")]),
?LI([?ACT("backup/", "Backup Management")]),
?LI([?ACT("ports/", "Listened Ports Management")]),
?LI([?ACT("stats/", "Statistics")])
[?LI([?ACT("db/", "Database")]),
?LI([?ACT("backup/", "Backup")]),
?LI([?ACT("ports/", "Listened Ports")]),
?LI([?ACT("stats/", "Statistics")]),
?LI([?ACT("update/", "Update")])
]),
?XAE("form", [{"action", ""}, {"method", "post"}],
[?INPUTT("submit", "restart", "Restart"),
@@ -1852,13 +1853,13 @@ get_node(global, Node, [], Query, Lang) ->
get_node(Host, Node, [], Query, Lang) ->
[?XC("h1", ?T("Node ") ++ atom_to_list(Node)),
?XE("ul",
[?LI([?ACT("modules/", "Modules Management")])])
[?LI([?ACT("modules/", "Modules")])])
];
get_node(global, Node, ["db"], Query, Lang) ->
case rpc:call(Node, mnesia, system_info, [tables]) of
{badrpc, _Reason} ->
[?XCT("h1", "RPC call error")];
[?XCT("h1", "RPC Call Error")];
Tables ->
Res = node_db_parse_query(Node, Tables, Query),
STables = lists:sort(Tables),
@@ -1896,10 +1897,10 @@ get_node(global, Node, ["db"], Query, Lang) ->
integer_to_list(Memory))
])
end, STables),
[?XC("h1", ?T("DB Tables at ") ++ atom_to_list(Node))] ++
[?XC("h1", ?T("Database Tables at ") ++ atom_to_list(Node))] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
@@ -1924,19 +1925,20 @@ get_node(global, Node, ["db"], Query, Lang) ->
get_node(global, Node, ["backup"], Query, Lang) ->
Res = node_backup_parse_query(Node, Query),
[?XC("h1", ?T("Backup Management at ") ++ atom_to_list(Node)),
[?XC("h1", ?T("Backup of ") ++ atom_to_list(Node)),
?XCT("p", "Remark that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately."),
?XAE("form", [{"action", ""}, {"method", "post"}],
[?XAE("table", [],
[?XE("tbody",
[?XE("tr",
[?XCT("td", "Store a backup in a file"),
[?XCT("td", "Store binary backup:"),
?XE("td", [?INPUT("text", "storepath",
"ejabberd.backup")]),
?XE("td", [?INPUTT("submit", "store",
"OK")])
]),
?XE("tr",
[?XCT("td", "Restore a backup from a file"),
[?XCT("td", "Restore binary backup immediately:"),
?XE("td", [?INPUT("text", "restorepath",
"ejabberd.backup")]),
?XE("td", [?INPUTT("submit", "restore",
@@ -1944,21 +1946,21 @@ get_node(global, Node, ["backup"], Query, Lang) ->
]),
?XE("tr",
[?XCT("td",
"Install a database fallback from a file"),
"Restore binary backup after next ejabberd restart (requires less memory):"),
?XE("td", [?INPUT("text", "fallbackpath",
"ejabberd.backup")]),
?XE("td", [?INPUTT("submit", "fallback",
"OK")])
]),
?XE("tr",
[?XCT("td", "Dump a database in a text file"),
[?XCT("td", "Store plain text backup:"),
?XE("td", [?INPUT("text", "dumppath",
"ejabberd.dump")]),
?XE("td", [?INPUTT("submit", "dump",
"OK")])
]),
?XE("tr",
[?XCT("td", "Restore a database from a text file"),
[?XCT("td", "Restore plain text backup immediately:"),
?XE("td", [?INPUT("text", "loadpath",
"ejabberd.dump")]),
?XE("td", [?INPUTT("submit", "load",
@@ -1981,8 +1983,8 @@ get_node(global, Node, ["ports"], Query, Lang) ->
rpc:call(Node, ejabberd_config, get_local_option, [listen])),
[?XC("h1", ?T("Listened Ports at ") ++ atom_to_list(Node))] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
@@ -2004,8 +2006,8 @@ get_node(Host, Node, ["modules"], Query, Lang) when is_list(Host) ->
rpc:call(Node, gen_mod, loaded_modules_with_opts, [Host])),
[?XC("h1", ?T("Modules at ") ++ atom_to_list(Node))] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
@@ -2028,35 +2030,67 @@ get_node(global, Node, ["stats"], Query, Lang) ->
TransactionsLogged =
rpc:call(Node, mnesia, system_info, [transaction_log_writes]),
[?XC("h1", io_lib:format(?T("~p statistics"), [Node])),
[?XC("h1", io_lib:format(?T("Statistics of ~p"), [Node])),
?XAE("table", [],
[?XE("tbody",
[?XE("tr", [?XCT("td", "Uptime"),
[?XE("tr", [?XCT("td", "Uptime:"),
?XAC("td", [{"class", "alignright"}],
UpTimeS)]),
?XE("tr", [?XCT("td", "CPU Time"),
?XE("tr", [?XCT("td", "CPU Time:"),
?XAC("td", [{"class", "alignright"}],
CPUTimeS)]),
?XE("tr", [?XCT("td", "Authenticated users"),
?XE("tr", [?XCT("td", "Authenticated Users:"),
?XAC("td", [{"class", "alignright"}],
integer_to_list(Users))]),
?XE("tr", [?XCT("td", "Transactions commited"),
?XE("tr", [?XCT("td", "Transactions Commited:"),
?XAC("td", [{"class", "alignright"}],
integer_to_list(TransactionsCommited))]),
?XE("tr", [?XCT("td", "Transactions aborted"),
?XE("tr", [?XCT("td", "Transactions Aborted:"),
?XAC("td", [{"class", "alignright"}],
integer_to_list(TransactionsAborted))]),
?XE("tr", [?XCT("td", "Transactions restarted"),
?XE("tr", [?XCT("td", "Transactions Restarted:"),
?XAC("td", [{"class", "alignright"}],
integer_to_list(TransactionsRestarted))]),
?XE("tr", [?XCT("td", "Transactions logged"),
?XE("tr", [?XCT("td", "Transactions Logged:"),
?XAC("td", [{"class", "alignright"}],
integer_to_list(TransactionsLogged))])
])
])];
get_node(global, Node, ["update"], Query, Lang) ->
rpc:call(Node, code, purge, [ejabberd_update]),
Res = node_update_parse_query(Node, Query),
rpc:call(Node, code, load_file, [ejabberd_update]),
{ok, Dir, UpdatedBeams, Script, LowLevelScript, Check} =
rpc:call(Node, ejabberd_update, update_info, []),
Mods =
case UpdatedBeams of
[] ->
?CT("None");
_ ->
?XE("ul",
[?LI([?C(atom_to_list(Beam))]) ||
Beam <- UpdatedBeams])
end,
FmtScript = ?XC("pre", io_lib:format("~p", [Script])),
FmtLowLevelScript = ?XC("pre", io_lib:format("~p", [LowLevelScript])),
[?XC("h1", ?T("Update ") ++ atom_to_list(Node))] ++
case Res of
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
[?INPUTT("submit", "update", "Update"),
?XCT("h2", "Update plan"),
?XCT("h3", "Updated modules"), Mods,
?XCT("h3", "Update script"), FmtScript,
?XCT("h3", "Low level update script"), FmtLowLevelScript,
?XCT("h3", "Script check"), ?C(atom_to_list(Check))])
];
get_node(Host, Node, NPath, Query, Lang) ->
[?XCT("h1", "Not found")].
[?XCT("h1", "Not Found")].
node_parse_query(Node, Query) ->
@@ -2330,6 +2364,22 @@ node_modules_parse_query(Host, Node, Modules, Query) ->
end.
node_update_parse_query(Node, Query) ->
case lists:keysearch("update", 1, Query) of
{value, _} ->
case rpc:call(Node, ejabberd_update, update, []) of
{ok, _} ->
ok;
{error, Error} ->
?ERROR_MSG("~p~n", [Error]);
{badrpc, Error} ->
?ERROR_MSG("~p~n", [Error])
end;
_ ->
nothing
end.
pretty_print(El) ->
lists:flatten(pretty_print(El, "")).
@@ -2395,10 +2445,10 @@ list_shared_roster_groups(Host, Query, Lang) ->
]
)]
)]),
[?XC("h1", ?T("Shared roster groups"))] ++
[?XC("h1", ?T("Shared Roster Groups"))] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],
@@ -2494,11 +2544,11 @@ shared_roster_group(Host, Group, Query, Lang) ->
]
)]
)]),
[?XC("h1", ?T("Shared roster groups"))] ++
[?XC("h1", ?T("Shared Roster Groups"))] ++
[?XC("h2", ?T("Group ") ++ Group)] ++
case Res of
ok -> [?CT("submitted"), ?P];
error -> [?CT("bad format"), ?P];
ok -> [?CT("Submitted"), ?P];
error -> [?CT("Bad format"), ?P];
nothing -> []
end ++
[?XAE("form", [{"action", ""}, {"method", "post"}],

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