Let’s see how to configure your ejabberd to enable this gateway.
First, add an HTTP handler, as Matrix uses HTTPS for Server-Server API.
In the listen
section of your ejabberd.yml
configuration file, add a handler on Matrix port 8448
for path /_matrix
that calls the mod_matrix_gw
module. You must enable TLS on this port to accept HTTPS connections (unless a proxy already handles HTTPS in front of ejabberd) and provide a valid certificate for your Matrix domain (see matrix_domain
below). You can set this certificate using the certfile
option of the listener, like in the example below, or listing it in the certfiles
top level option.
Example:
listen:
-
port: 5222
module: ejabberd_c2s
-
port: 8448 # Matrix federation
module: ejabberd_http
tls: true
certfile: "/opt/ejabberd/conf/matrix.pem"
request_handlers:
"/_matrix": mod_matrix_gw
If you want to use a non-standard port instead of 8448
, you must serve a /.well-known/matrix/server
on your Matrix domain (see below).
You must enable s2s (Server-to-Server federation) by setting an access rule all
or allow
on s2s_access top level option:
Example:
s2s_access: s2s
access_rules:
local:
- allow: local
c2s:
- deny: blocked
- allow
s2s:
- allow # to allow Matrix federation
Finally, add mod_matrix_gw
module in the modules list.
Example:
modules:
mod_matrix_gw:
matrix_domain: "matrixdomain.com"
key_name: "key1"
key: "SU4mu/j8b8A1i1EdyxIcKlFlrp+eSRBIlZwGyHP7Mfo="
matrix_domain
Replace matrixdomain.com
with your Matrix domain. That domain must resolve to your ejabberd server or serve a file https://matrixdomain.com/.well-known/matrix/server that contains a JSON file with the address and Matrix port (as defined by the Matrix HTTPS handler, see above) of your ejabberd server:
Example:
{
"m.server": "ejabberddomain.com:8448"
}
key_name
& key
The key_name
is arbitrary. The key
value is your base64-encoded ed25519 Matrix signing key. It can be generated by Matrix tools or in an Erlang shell using the command base64:encode(element(2, crypto:generate_key(eddsa, ed25519))).
:
Example:
$ erl
Erlang/OTP 24 [erts-12.3.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [dtrace]
Eshell V12.3.1 (abort with ^G)
1> base64:encode(element(2, crypto:generate_key(eddsa, ed25519))).
<<"SU4mu/j8b8A1i1EdyxIcKlFlrp+eSRBIlZwGyHP7Mfo=">>
2> q().
ok
Once your configuration is ready, you can restart ejabberd.
To check if your setup is correct, go to the following page and enter your Matrix domain (as set by the matrix_domain
option):
https://federationtester.matrix.org/
This page should list any problem related to Matrix on your ejabberd installation.
What messages are routed to an external Matrix server?
Let’s say an XMPP client connected to your ejabberd server sends a message to a JID user1@domain1.com
. If domain1.com
is defined by the hosts
parameter of your ejabberd server (i.e. it’s one of your XMPP domains), the message will be routed locally. If it’s not, ejabberd will try to establish an XMPP Server-to-Server connection to a remote domain1.com
XMPP server. If this fails (i.e. there is no such external domain1.com
XMPP domain), then ejabberd will try on the Matrix federation, transforming the user1@domain1.com
JID into the Matrix ID @user1:domain1.com
and will try to open a connection to a remote domain1.com
Matrix domain.
It is also possible to route messages explicitly to the Matrix federation by setting the option matrix_id_as_jid
in the mod_matrix_gw
module to true
:
Example:
modules:
mod_matrix_gw:
host: "matrix.@HOST@"
matrix_domain: "matrixdomain.com"
key_name: "key1"
key: "SU4mu/j8b8A1i1EdyxIcKlFlrp+eSRBIlZwGyHP7Mfo="
matrix_id_as_jid: true
In this case, the automatic fallback to Matrix when XMPP s2s fails is disabled and messages must be explicitly sent to the matrix gateway service Jabber ID to be routed to a remote Matrix server.
To send a message to the Matrix user @user:remotedomain.com
, the XMPP client must send a message to the JID user%remotedomain.com@matrix.xmppdomain.com
, where matrix.xmppdomain.com
is the JID of the gateway service as set by the host
option of the mod_matrix_gw
module (the keyword @HOST@
is replaced with the XMPP domain of the server). If host
is not set, the Matrix gateway JID is your XMPP domain with the matrix.
prefix added.
The default value for matrix_id_as_jid
is false
, so the implicit routing will be used if this option is not set.
ejabberd 24.02 has just been release and well, this is a huge release with 200 commits and more in the libraries. We’ve packed this update with a plethora of new features, significant improvements, and essential bug fixes, all designed to supercharge your messaging infrastructure.
– 🌐 Matrix Federation Unleashed: Imagine seamlessly connecting with Matrix servers – it’s now possible! ejabberd breaks new ground in cross-platform communication, fostering a more interconnected messaging universe. We have still some ground to cover and for that we are waiting for your feedback.
– 🔐 Cutting-Edge Security with TLS 1.3 & SASL2: In an era where security is paramount, ejabberd steps up its game. With support for TLS 1.3 and advanced SASL2 protocols, we increase the overall security for all platform users.
– 🚀 Performance Enhancements with Bind 2: Faster connection times, especially crucial for mobile network users, thanks to Bind 2 and other performance optimizations.
– 🔄 User gains better control over on their messages: The new support for XEP-0424: Message Retraction allows users to manage their message history and remove something they posted by mistake.
– 🔧 Optimized server pings by relying on an existing mechanism coming from XEP-0198
– 📈 Streamlined API Versioning: Our refined API versioning means smoother, more flexible integration for your applications.
– 🧩 Enhanced Elixir, Mix and Rebar3 Support
If you upgrade ejabberd from a previous release, please review those changes:
A more detailed explanation of those topics and other features:
ejabberd is now able to federate with Matrix servers. Detailed instructions to setup Matrix federation with ejabberd will be detailed in another post.
Here is a quick summary of the configuration steps:
First, s2s must be enabled on ejabberd. Then define a listener that uses mod_matrix_gw
:
listen:
-
port: 8448
module: ejabberd_http
tls: true
certfile: "/opt/ejabberd/conf/server.pem"
request_handlers:
"/_matrix": mod_matrix_gw
And add mod_matrix_gw
in your modules:
modules:
mod_matrix_gw:
matrix_domain: "domain.com"
key_name: "somename"
key: "yourkeyinbase64"
With the new support for XEP-0424: Message Retraction, users of MAM message archiving can control their message archiving, with the ability to ask for deletion.
If stream management is enabled, let mod_ping trigger XEP-0198 <r/>equests
rather than sending XEP-0199 pings. This avoids the overhead of the ping IQ stanzas, which, if stream management is enabled, are accompanied by XEP-0198 elements anyway.
The table archive
has a text column named origin_id
(see commit 975681). You have two methods to update the SQL schema of your existing database:
If using MySQL or PosgreSQL, you can enable the option update_sql_schema
and ejabberd will take care to update the SQL schema when needed: add in your ejabberd configuration file the line update_sql_schema: true
If you are using other database, or prefer to update manually the SQL schema:
ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT '';
ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT;
CREATE INDEX i_archive_username_origin_id USING BTREE ON archive(username(191), origin_id(191));
ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT '';
ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT;
CREATE INDEX i_archive_sh_username_origin_id USING BTREE ON archive(server_host(191), username(191), origin_id(191))
ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT '';
ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT;
CREATE INDEX i_archive_username_origin_id ON archive USING btree (username, origin_id);
ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT '';
ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT;
CREATE INDEX i_archive_sh_username_origin_id ON archive USING btree (server_host, username, origin_id);
ALTER TABLE [dbo].[archive] ADD [origin_id] VARCHAR (250) NOT NULL;
CREATE INDEX [archive_username_origin_id] ON [archive] (username, origin_id)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
ALTER TABLE [dbo].[archive] ADD [origin_id] VARCHAR (250) NOT NULL;
CREATE INDEX [archive_sh_username_origin_id] ON [archive] (server_host, username, origin_id)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT '';
CREATE INDEX i_archive_username_origin_id ON archive (username, origin_id);
ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT '';
CREATE INDEX i_archive_sh_username_origin_id ON archive (server_host, username, origin_id);
This ejabberd release includes support for XEP-0474: SASL SCRAM Downgrade Protection, and some clients may not support it correctly yet.
If you are using Converse.js 10.1.6 or older, Movim 0.23 Kojima or older, or any other client based in Strophe.js v1.6.2 or older, you may notice that they cannot authenticate correctly to ejabberd.
To solve that problem, either update to newer versions of those programs (if they exist), or you can enable temporarily the option disable_sasl_scram_downgrade_protection
in the ejabberd configuration file ejabberd.yml
like this:
disable_sasl_scram_downgrade_protection: true
Until now, when a new ejabberd release changed some API command (an argument renamed, a result in a different format…), then you had to update your API client to the new API at the same time that you updated ejabberd.
Now the ejabberd API commands can have different versions, by default the most recent one is used, and the API client can specify the API version it supports.
In fact, this feature was implemented seven years ago, included in ejabberd 16.04, documented in ejabberd Docs: API Versioning… but it was never actually used!
This ejabberd release includes many fixes to get API versioning up to date, and it starts being used by several commands.
Let’s say that ejabberd 23.10 implemented API version 0, and this ejabberd 24.02 adds API version 1. You may want to update your API client to use the new API version 1… or you can continue using API version 0 and delay API update a few weeks or months.
To continue using API version 0:
– if using ejabberdctl, use the switch --version 0
. For example: ejabberdctl --version 0 get_roster admin localhost
– if using mod_http_api, in ejabberd configuration file add v0
to the request_handlers
path. For example: /api/v0: mod_http_api
Check the details in ejabberd Docs: API Versioning.
When you want to update your API client to support ejabberd API version 1, those are the changes to take into account:
– Commands with list arguments
– mod_http_api does not name integer and string results
– ejabberdctl with list arguments
– ejabberdctl list results
All those changes are described in the next sections.
Several commands now use list
argument instead of a string
with separators (different commands used different separators: ;
:
\\n
,
).
The commands improved in API version 1:
– add_rosteritem
– oauth_issue_token
– send_direct_invitation
– srg_create
– subscribe_room
– subscribe_room_many
For example, srg_create
in API version 0 took as arguments:
{"group": "group3",
"host": "myserver.com",
"label": "Group3",
"description": "Third group",
"display": "group1\\ngroup2"}
now in API version 1 the command expects as arguments:
{"group": "group3",
"host": "myserver.com",
"label": "Group3",
"description": "Third group",
"display": ["group1", "group2"]}
There was an incoherence in mod_http_api results when they were integer/string and when they were list/tuple/rescode…: the result contained the name, for example:
$ curl -k -X POST -H "Content-type: application/json" -d '{}' "http://localhost:5280/api/get_loglevel/v0"
{"levelatom":"info"}
Staring in API version 1, when result is an integer or a string, it will not contain the result name. This is now coherent with the other result formats (list, tuple, …) which don’t contain the result name either.
Some examples with API version 0 and API version 1:
$ curl -k -X POST -H "Content-type: application/json" -d '{}' "http://localhost:5280/api/get_loglevel/v0"
{"levelatom":"info"}
$ curl -k -X POST -H "Content-type: application/json" -d '{}' "http://localhost:5280/api/get_loglevel"
"info"
$ curl -k -X POST -H "Content-type: application/json" -d '{"name": "registeredusers"}' "http://localhost:5280/api/stats/v0"
{"stat":2}
$ curl -k -X POST -H "Content-type: application/json" -d '{"name": "registeredusers"}' "http://localhost:5280/api/stats"
2
$ curl -k -X POST -H "Content-type: application/json" -d '{"host": "localhost"}' "http://localhost:5280/api/registered_users/v0"
["admin","user1"]
$ curl -k -X POST -H "Content-type: application/json" -d '{"host": "localhost"}' "http://localhost:5280/api/registered_users"
["admin","user1"]
ejabberdctl now supports list and tuple arguments, like mod_http_api and ejabberd_xmlrpc. This allows ejabberdctl to execute all the existing commands, even some that were impossible until now like create_room_with_opts
and set_vcard2_multi
.
List elements are separated with ,
and tuple elements are separated with :
.
Relevant commands:
– add_rosteritem
– create_room_with_opts
– oauth_issue_token
– send_direct_invitation
– set_vcard2_multi
– srg_create
– subscribe_room
– subscribe_room_many
Some example uses:
ejabberdctl add_rosteritem user1 localhost testuser7 localhost NickUser77l gr1,gr2,gr3 both
ejabberdctl create_room_with_opts room1 conference.localhost localhost public:false,persistent:true
ejabberdctl subscribe_room_many user1@localhost:User1,admin@localhost:Admin room1@conference.localhost urn:xmpp:mucsub:nodes:messages,u
Until now, ejabberdctl returned list elements separated with ;
. Now in API version 1 list elements are separated with ,
.
For example, in ejabberd 23.10:
$ ejabberdctl get_roster admin localhost
jan@localhost jan none subscribe group1;group2
tom@localhost tom none subscribe group3
Since this ejabberd release, using API version 1:
$ ejabberdctl get_roster admin localhost
jan@localhost jan none subscribe group1,group2
tom@localhost tom none subscribe group3
it is still possible to get the results in the old syntax, using API version 0:
$ ejabberdctl --version 0 get_roster admin localhost
jan@localhost jan none subscribe group1;group2
tom@localhost tom none subscribe group3
ejabberd supports around 200 administrative commands, and probably you consult them in the ejabberd Docs -> API Reference page, where all the commands documentation is perfectly displayed…
The ejabberdctl
command-line script already allowed to consult the commands documentation, consulting in real-time your ejabberd server to show you exactly the commands that are available. But it lacked some details about the commands. That has been improved, and now ejabberdctl
shows all the information, including arguments description, examples and version notes.
For example, the connected_users_vhost
command documentation as seen in the ejabberd Docs site is equivalently visible using ejabberdctl
:
$ ejabberdctl help connected_users_vhost
Command Name: connected_users_vhost
Arguments: host::binary : Server name
Result: connected_users_vhost::[ sessions::string ]
Example: ejabberdctl connected_users_vhost "myexample.com"
user1@myserver.com/tka
user2@localhost/tka
Tags: session
Module: mod_admin_extra
Description: Get the list of established sessions in a vhost
Erlang/OTP 27.0-rc1 was recently released, and ejabberd can be compiled with it. If you are developing or experimenting with ejabberd, it would be great if you can use Erlang/OTP 27 and report any problems you find. For production servers, it’s recommended to stick with Erlang/OTP 26.2 or any previous version.
In this sense, the rebar
and rebar3
binaries included with ejabberd are also updated: now they support from Erlang 24 to Erlang 27. If you want to use older Erlang versions from 20 to 23, there are compatible binaries available in git: rebar from ejabberd 21.12 and rebar3 from ejabberd 21.12.
Of course, if you have rebar
or rebar3
already installed in your system, it’s preferable if you use those ones, because probably they will be perfectly compatible with whatever erlang version you have installed.
ejabberd
container imageThe binary installers now include the recent and stable Erlang/OTP 26.2.2 and Elixir 1.16.1. Many other dependencies were updated in the installers, the most notable is OpenSSL that has jumped to version 3.2.1.
The ejabberd
container image and the ecs
container image have gotten all those version updates, and also Alpine is updated to 3.19.
By the way, this container image already had support to run commands when the container starts… And now you can setup the commands to allow them fail, by prepending the character !
.
When compiling ejabberd from source code, you may have noticed there are a lot of possibilities. Let’s take an overview before digging in the new improvements:
make install
: copies the files to the systemmake prod
: prepares a self-contained OTP production release in _build/prod/
, and generates a tar.gz
file. This was previously named make rel
make dev
: prepares quickly an OTP development release in _build/dev/
make relive
: prepares the barely minimum in _build/relive/
to run ejabberd and starts itejabberdctl
with erlang shell: start
/foreground
/live
ejabberdctl
with elixir shell: iexlive
ejabberd
console
/start
(this script is generated by rebar3 or mix, and does not support ejabberdctl configurable options)For example:
– the CI
dynamic tests use rebar3
, and Runtime
tries to test all the possible combinations
– ejabberd binary installers are built using: mix + make prod
– container images are built using: mix + make prod
too, and started with ejabberdctl foreground
Several combinations didn’t work correctly until now and have been fixed, for example:
– mix + make relive
– mix + make prod/dev + ejabberdctl iexlive
– mix + make install + ejabberdctl start/foregorund/live
– make uninstall
buggy has an experimental alternative: make uninstall-rel
– rebar + make prod
with Erlang 26
ejabberd uses Rebar to manage dependencies and compilation since ejabberd 13.10 4d8f770. However, that tool is obsolete and unmaintained since years ago, because there is a complete replacement:
Rebar3 is supported by ejabberd since 20.12 0fc1aea. Among other benefits, this allows to download dependencies from hex.pm and cache them in your system instead of downloading them from git every time, and allows to compile Elixir files and Elixir dependencies.
In fact, ejabberd can be compiled using mix
(a tool included with the Elixir programming language) since ejabberd 15.04 ea8db99 (with improvements in ejabberd 21.07 4c5641a)
For those reasons, the tool selection performed by ./configure
will now be:
– If --with-rebar=rebar3
but Rebar3 not found installed in the system, use the rebar3
binary included with ejabberd
– Use the program specified in option: --with-rebar=/path/to/bin
– If none is specified, use the system mix
– If Elixir not found, use the system rebar3
– If Rebar3 not found, use the rebar3
binary included with ejabberd
Support for Elixir 1.1 was added as a dependency in commit 01e1f67 to ejabberd 15.02. This allowed to compile Elixir files. But since Elixir 1.4.5 (released Jun 22, 2017) it isn’t possible to get Elixir as a dependency… it’s nowadays a standalone program. For that reason, support to download old Elixir 1.4.4 as a dependency has been removed.
When Elixir support is required, better simply install Elixir and use mix
as build tool:
./configure --with-rebar=mix
Or install Elixir and use the experimental Rebar3 support to compile Elixir files and dependencies:
./configure --with-rebar=rebar3 --enable-elixir
It is now possible to compile ejabberd using Rebar3 and support Elixir compilation. This compiles the Elixir files included in ejabberd’s lib/
path. There’s also support to get dependencies written in Elixir, and it’s possible to build OTP releases including Elixir support.
It is necessary to have Elixir installed in the system, and configure the compilation using --enable-elixir
. For example:
apt-get install erlang erlang-dev elixir
git clone https://github.com/processone/ejabberd.git ejabberd
cd ejabberd
./autogen.sh
./configure --with-rebar=rebar3 --enable-elixir
make
make dev
_build/dev/rel/ejabberd/bin/ejabberdctl iexlive
Elixir 1.10.3 is the minimum supported, but:
– Elixir 1.10.3 or higher is required to build an OTP release with make prod
or make dev
– Elixir 1.11.4 or higher is required to build an OTP release if using Erlang/OTP 24 or higher
– Elixir 1.11.0 or higher is required to use make relive
– Elixir 1.13.4 with Erlang/OTP 23.0 are the lowest versions tested by Runtime
For all those reasons, if you want to use Elixir, it is highly recommended to use Elixir 1.13.4 or higher with Erlang/OTP 23.0 or higher.
make rel
is renamed to make prod
When ejabberd started to use Rebar2 build tool, that tool could create an OTP release, and the target in Makefile.in
was conveniently named make rel
.
However, newer tools like Rebar3 and Elixir’s Mix support creating different types of releases: production, development, … In this sense, our make rel
target is nowadays more properly named make prod
.
For backwards compatibility, make rel
redirects to make prod
.
make install-rel
and make uninstall-rel
This is an alternative method to install ejabberd in the system, based in the OTP release process. It should produce exactly the same results than the existing make install
.
The benefits of make install-rel
over the existing method:
– this uses OTP release code from rebar/rebar3/mix, and consequently requires less code in our Makefile.in
– make uninstall-rel
correctly deletes all the library files
This is still experimental, and it would be great if you are able to test it and report any problem; eventually this method could replace the existing one.
Just for curiosity:
– ejabberd 13.03-beta1 got support for make uninstall
was added
– ejabberd 13.10 introduced Rebar build tool and code got more modular
– ejabberd 15.10 started to use the OTP directory structure for ‘make install’, and this broke make uninstall
We would like to thank the contributions to the source code, documentation, and translation provided for this release by:
And also to all the people contributing in the ejabberd chatroom, issue tracker…
Customers of the ejabberd Business Edition, in addition to all those improvements and bugfixes, also get:
x:oob
data as source for image delivered in pushesTooManyProviderTokenUpdated
errorget_push_logs
command generate better error if mod_push_logger
not availableget_push_logs
that can be used to retrieve info about recent pushes and errors reported by push servicesmod_mam_http_access
API to also accept range of messagesmod_muc_state_query
to fix subject_author
room state fieldxdata
in mod_muc_state_query
This is a more detailed list of changes in this ejabberd release:
mod_matrix_gw
disable_sasl_scram_downgrade_protection
: New option to disable XEP-0474negotiation_timeout
: Increase default value from 30s to 2mEJABBERD_OPTS
and logger options""
, and use previous flags as exampleeldap tls_verify=soft
and false
fail_if_no_peer_cert
for eldap ssl client connectionsmax_items
node options on readsha256_password
auth pluginsql_flags
: right now only useful to enable mysql_alternative_upsert
CTL_ON_*
commandsprint_sql_schema
: New command available in ejabberdctl command-line scriptmuc_sub
tag to all the relevant commandsset_presence
: switch priority argument from string to integervX
--enable-group
option (#4135)--enable-tools
install-rel
and uninstall-rel
make rel
to make prod
make edoc
to use ExDoc, requires mixescript
to run rebar|rebar3|mix--with-rebar=rebar3
but rebar3 not system-installed, use local one--enable-elixir
iex
dev
profile/environment, enable tools automaticallyFORMATTER ERROR: bad return value
(#4087)Elixir Hex API
vars.config
not foundvars_config_path
to set path to vars.config
(#4128)https://github.com/processone/ejabberd/compare/23.10…24.02
As usual, the release is tagged in the Git source code repository on GitHub.
The source package and installers are available in ejabberd Downloads page. To check the *.asc
signature files, see How to verify ProcessOne downloads integrity.
For convenience, there are alternative download locations like the ejabberd DEB/RPM Packages Repository and the GitHub Release / Tags.
The ecs
container image is available in docker.io/ejabberd/ecs and ghcr.io/processone/ecs. The alternative ejabberd
container image is available in ghcr.io/processone/ejabberd.
If you consider that you’ve found a bug, please search or fill a bug report on GitHub Issues.
The post ejabberd 24.02 first appeared on ProcessOne.]]>To enable it, go to your services in your fluux.io console, select “Push Notifications” and then “+ WebPush”
You will be prompted for an appid (typically the domain you want to enable WebPush on). For example here fluux.io. It will generate a VAPID key that will be used by ejabberd to sign the push notification sent to the user’s browser.
Checking “View Config” will allow you to see the VAPID public key. It will be required to let the browser subscribe to notifications. Your website also needs to register a service worker that will be responsible for displaying the notification when a push is received.
As an example, we provide a small ejabberd client to test the whole workflow. It is pre-populated with a test user and associated appid/key.
The first step is to authenticate an XMPP user through your service. Then click “Enable Push“.
It will ask authorization to enable push notification and create a subscription to FCM/Apple/Mozilla services. Then the XMPP client (using strophe.js) will send a stanza to enable offline messaging. ejabberd will now send a notification to this entry point, which will send a push to the user’s browser.
To trigger it, disconnect/close all opened XMPP sessions of your test user and send him a message from another test user. Your browser will display a notification from your website with the message snippet and its author.
Alternatively, you can check the test user and its associated devices:
and send a test notification:
The post WebPush support on your fluux.io instance first appeared on ProcessOne.]]>Time flies, and it’s hard to believe that ejabberd, our beloved open-source project, celebrated its 21st anniversary last November 16th! It’s a milestone that we’ve proudly highlighted over the years – remember the 4th, 10th, 18th, and 20th anniversaries? Well, 21 is just as significant, marking over two decades of innovation and community effort.
In honor of this remarkable journey, we’ve launched a new section on the ejabberd Docs site, showcasing a timeline of all ejabberd releases alongside their major advancements. This walk down memory lane is not just nostalgic, but a testament to continuous improvement and adaptation. Check out the ejabberd roadmap to witness our evolutionary path.
Reflecting on the past year, ejabberd has seen substantial growth and development with three key releases:
As we step into the new year, our roadmap is already filled with exciting updates. The upcoming ejabberd releases are set to include:
But that’s not all. In our continuous effort to share the advancements of ejabberd Business Edition with a wider audience, we have some exciting news for early 2024:
We will also be adding significant scalability improvements in ejabberd Business Edition:
As we reflect on our milestones and look ahead, we owe immense gratitude to our community. Your support and feedback are the pillars of ejabberd’s success.
Looking into 2024, we’re set to strengthen ejabberd’s presence and affirm ProcessOne’s role as the “invisible leader” in Instant Messaging. With exciting developments on the horizon, we’re eager to make the next year a landmark for ejabberd.
Your continued feedback is vital as we forge ahead. Together, let’s shape 2024 into a remarkable year for ejabberd and our community.
Thank you for being part of this journey. Here’s to an innovative 2024!
The post Happy New Year: Celebrating 21 Years of Innovation with ejabberd first appeared on ProcessOne.]]>Thirty years after the advent of the first instant messaging services, we still haven’t reached the stage where instant messaging platforms can freely communicate with each other, as is the case with email. In 1999, the Jabber/XMPP protocol was created and standardized for this purpose by the Internet Engineering Task Force (IETF). Since then, proprietary messaging services have continuously leveraged the power of internet giants to dominate the market. Why do neither XMPP nor the more recent Matrix, which aimed to improve upon it, break through this barrier, when it’s clear that protocols must be open to enable exchange? Without this fundamental principle, the Internet itself wouldn’t exist.
In the following article, I revisit how the French government recently promoted the instant messaging service Olvid and what this reveals about our approach to digital technology. It’s frustrating to see France promote a secure, yet proprietary messaging service that offers no progress in terms of interoperability, especially at a time when the European Union is striving to open up the sector by requiring all messaging services to be capable of intercommunication, through the Digital Markets Act.
I conclude with reflections on our inability in Europe to collaborate on “commons,” our difficulty in building a foundation, an ecosystem that allows for healthy co-opetition, a blend of competition and collaboration, which is the only way to regain significance in the digital economy. Short-term political thinking forces our companies into an every-man-for-himself approach, preferring to dominate a small market rather than share a larger one.
Today, perhaps, it’s time for a change?
Thirty years and counting since the emergence of the first instant messaging services, we still lack a universally accepted exchange protocol, as is the case with email. The Jabber protocol, later renamed XMPP (eXtensible Messaging and Presence Protocol) and made a standard, was born with the hope of breaking the proliferation of isolated silos like MSN, ICQ, Yahoo!, which did not communicate with each other. Today, other silos have emerged, but the problem persists: it is still impossible to exchange messages between accounts from different major messaging providers. Why? Let me tell you the story of a clumsy communication operation around a French messaging service, Olvid, which illustrates well the familiar patterns we often find ourselves stuck in.
I discovered the messaging service Olvid in late November 2023, following a flood of articles in the French press. I wondered how a company of 15 employees, created in 2019, had managed to get such press coverage. It was promoted directly by Prime Minister Elisabeth Borne: “Popular messaging applications like WhatsApp, Telegram or Signal have ‘security flaws’,” justified the office of Elisabeth Borne, who urged her ministers to download the French application.” (Les Échos, November 30, 2023). In November 2023, Matignon asked government members and ministerial offices to install this system on their phones and computers “to replace other instant messaging services to enhance the security of exchanges.” Then came the superlatives: “The most secure messaging service in the world” (Jean-Noël Barrot). “A step towards greater French sovereignty” (Elisabeth Borne). And it needs to be done quickly. Elisabeth Borne asked ministers to “take all necessary steps” to deploy Olvid in their ministry “by December 8, 2023, at the latest” (Ouest France, November 29, 2023).
Why Olvid? The articles I read on the subject remain relatively vague; I know mainly that it is certified by ANSII, the organization guaranteeing the state’s IT security. Yet, it’s far from the first secure messaging service I’ve come across, and it’s the first time I’ve heard of Olvid. What about other services and especially Signal, which is recognized worldwide for its security, backed by audits? Among secure messengers, the list is long: Signal, Threema, Wire, Berty, etc. So, what security flaws are we talking about?
Signal’s response was swift, with a direct and clear position from Meredith Whittaker, president of the Signal Foundation:
The French PM is mandating ministers use a small French messaging app. OK. But I’m alarmed that she’s claiming “security flaws” in Signal (et al) to justify the move. This claim is not backed by any evidence, and is dangerously misleading esp. coming from gov.
If you want to use a French product go for it! But don’t spread misinfo in the process. Signal is independently audited, open source, and our protocol has been tested for >10yrs. We are serious about responsible disclosure and we prioritize all reports to security@signal.org
Numérama, December 1, 2023
Regarding Olvid’s security, the main argument seems to be as follows: The system does not rely on centralized directories, operates without identifiers, which means no user account is hosted in the cloud.
First, it seems to me that this is the principle of key-based authentication. Message routing is done solely based on a key, in the cryptographic sense. If it is lost, it’s impossible to recover the account. Nothing revolutionary, then; it’s cryptography, dating back to the encryption software PGP (Pretty Good Privacy) of the 1990s and even before.
Then, such a system generally requires the physical exchange of public keys. Where Olvid seems to stand out is in the alternative ways proposed to simplify and lighten the burden of key exchange by meeting physically. This can work, first because the product is not free, so the user base is limited, where Signal, for example, offers a global platform and says it needs an identifier, the phone number to limit spam. Then, these alternative methods rely on mobile device management (MDM) tools, interfacing with an enterprise version of the Olvid server. In one way or another, this goes through a central point of distribution and reintroduces a weakness. It’s far from a completely decentralized protocol like what the team building the Berty messaging service is trying to do, for instance.
Browsing their site to find the protocol, I admit I choked a bit on some mentions thrown a little freely on their site, for example, Post Quantum Cryptography, cryptography that resists quantum computing. It’s nice, it’s pleasant, but in practice, what’s the reality? I didn’t find more detail under this mention, but personally, being hit with such buzzwords makes me rather flee, as it smells of a commercial who got a bit carried away. But let’s assume, the Olvid team is composed of encryption experts. I skimmed their specifications, but I admit I’m not a mathematician, so who am I to judge their math formulas?
What I do understand, however, is that almost all secure messaging systems, including Olvid, rely on the Double Ratchet algorithm, which was first introduced by… Signal.
In terms of protocol, however, I am an expert. I have been working on instant messaging protocols since 1999. And, it’s not beautiful… Olvid’s protocol is the antithesis of what I would like to see in an ambitious messaging protocol. It is a proprietary, ad hoc protocol, not based on any standard, minimalist for now, and condemns itself to reinventing the wheel, poorly. The burning question is, why not choose an open protocol that already works on a large scale, like XMPP, adding their value on top? The Internet protocol, TCP/IP, is open, all machines in the world can communicate, yet there are competing internet service providers. I am still looking for an answer. Because XMPP is too complex, some will say? I think any sufficiently advanced chat protocol tends to become a derivative of XMPP, less accomplished. Come on, why not even use Matrix, a competing protocol to my favorite? Apart from simple ignorance, I see no reason. Unless it’s to lock down the platform, perhaps? But, locking a communication protocol makes no sense. It’s replaying the battle of internet protocols, TCP/IP versus X.25. A communication protocol is meant to be open and interoperable. Personally, I would invite Olvid to adopt a messaging standard. Let them turn to the W3C or IETF, to XMPP or MLS. These organizations do good work. And it’s a guarantee of sustainability and above all, of interoperability.
We come to a very sore point. The European Commission, and therefore France as well, is discussing the implementation of the Digital Market Act. Among the points the European Union wants to impose is… the interoperability of instant messaging services. How can the French government promote a messaging solution that is not interoperable? And preferably standardized and open.
I talked about Olvid’s proprietary protocol, which is actually more of an API (Application Programming Interface), that is, a document that describes how to automate certain functions of their server. What about the implementation? The client is open source (on iOS and Android), but seeing in their exchange interface calls to URLs named /Freetrial. This implies payment. I am not sure that Olvid would welcome the idea of compiling and deploying one’s own version of the client. That’s the principle of Open Source, but such an initiative could try to circumvent payments to Olvid. As anyway, no open-source server is available and the only one running is operated by Olvid, the client code is of little use. Especially since the client code is published by Olvid, but to what extent can we know if it is 100% identical to the version distributed in the iOS and Android app stores? We don’t really have a way of knowing.
I know that Olvid promises one day to release the server as Open Source. What I’ve seen of the protocol, their business model, and what they say about their implementation, very tied to the Amazon infrastructure (an infrastructure managed by an American company, so much for sovereignty), makes me think that this will not happen, at least not for a very long time. I hope, of course, to be wrong.
In the meantime? I would really like us to be serious about instant messaging, that finally all players in the sector row in the same direction, those who work on open protocols, offering free servers and clients, that we build real collaboration, worthy of the construction of internet protocols, to build the foundation of a universal, open, open-source and truly interoperable messaging service. It doesn’t take much, to develop the culture of “coopetition,” collaboration around a common good between competing companies.
Found a mistake? I’m not perfect and would be happy to correct it. Contact us!
— Photo by Steve Johnson on Unsplash
The post Instant Messaging: Protocols are “Commons”, Let’s Take Them Seriously first appeared on ProcessOne.]]>
Previously, if you were using ejabberd with an external relational database, you might have to manually apply some schema changes that come with new features when you upgrade to a new ejabberd release. ejabberd can now handle this schema upgrade automatically. It can also create the schema on an empty database during a new deployment. It works with both old and new schemas.
This feature paves the way for more changes to our schema in the future. It is currently in beta testing, we recommend backing up your database before using it. To enable it in ejabberd 23.10, set this top-level option in your ejabberd.yml
configuration file and restart ejabberd:
update_sql_schema: true
This is compatible with the following relational databases:
Feel free to test it and report any problems on GitHub Issues.
The post Automatic schema update in ejabberd first appeared on ProcessOne.]]>A more detailed explanation of improvements and features:
XEP-0402: PEP Native Bookmarks describes how to keep a list of chatroom bookmarks as PEP nodes on the PubSub service. That’s an improvement over XEP-0048: Bookmark Storage which described how to store in a single Private XML Storage or a single PEP node.
mod_private
now supports the bookmark conversion described in XEP-0402:
ejabberd synchronizes XEP-0402 bookmarks, private storage bookmarks and XEP-0048 bookmarks.
In this sense, the bookmarks_to_pep
command performs an initial synchronization of bookmarks, getting bookmarks from Private XML Storage and stores them in PEP nodes as described both in XEP-0048 and XEP-0402.
mod_muc_occupantid
module with support for XEP-0421: Occupant IdXEP-0421: Anonymous unique occupant identifiers for MUCs is useful in anonymous MUC rooms, message correction and message retractions. Right now the only client found to support XEP-0421 is Dino, since version 0.4.
ejabberd now implements XEP-0421 0.1.0 in mod_muc_occupantid
. The module is quite simple and has no configurable options: just enabled it in the modules
section in your ejabberd.yml
configuration file and restart
ejabberd or reload_config
.
auth_external_user_exists_check
The new option auth_external_user_exists_check
makes user_check
hook work better with authentication methods that don’t have a way to determine if user exists. This happens, for example, in the case of jwt and cert based authentication. As result, enabling this option improves mod_offline
and mod_mam
handling of offline messages to those users. This reuses information stored by mod_last
for this purpose.
Authentication methods that manage users list outside of ejabberd, like for example JWT token or tls certificate authentication, had issue with processing of offline messages. Those methods didn’t have a way to tell if given user existed when user was not logged in, and that did block processing of offline messages, which were only performed for users that we know did exists. This release adds code that also consults data stored by mod_last
for that purpose, and it should fix offline messages for users that were logged at least once before.
get_roster
commandThere are some changes in the result output of the get_roster
command defined in mod_admin_extra
:
ask
is renamed to pending
group
is renamed to groups
groups
is a list with all the group namesFor example, let’s say that admin@localhost
has two contacts: a contact is present in two groups (group1
and group2
), the other contact is only present in a group (group3
).
The old get_roster command in ejabberd 23.04 and previous versions was like:
$ ejabberdctl get_roster admin localhost
jan@localhost jan none subscribe group1
jan@localhost jan none subscribe group2
tom@localhost tom none subscribe group3
The new get_roster command in ejabberd 23.XX and newer versions returns as result:
$ ejabberdctl get_roster admin localhost
jan@localhost jan none subscribe group1;group2
tom@localhost tom none subscribe group3
Notice that the ejabberdctl
command-line tool since now will represent list elements in results separated with ;
halt
commandUntil now there were two API commands to stop ejabberd:
stop
stops ejabberd gracefully, calling to stop each of its components (client sessions, modules, listeners, …)stop_kindly
first of all sends messages to all the online users and all the online MUC rooms, waits a few seconds, and then stops ejabberd gracefully.Those comands are useful when there’s an ejabberd running for many time, with many users connected, and you want to stop it.
A new command is now added: halt
, which abruptly stops the ejabberd node, without taking care to close gracefully any of its components. It also returns error code 1
. This command is useful if some problem is detected while ejabberd is starting.
For example, it is now used in the ecs
and the ejabberd
container images when CTL_ON_CREATE
or CTL_ON_START
were provided and failed to execute correctly. See docker-ejabberd#97 for details.
MySQL driver will now use prepared statements whenever possible, this should improve database load. This feature can be disabled with sql_prepared_statement: false
.
We also added alternative implementation of upsert that doesn’t use replace ..
or insert ... on conflict update
, as in some versions of MySQL this can lead to excessive deadlocks. We switch between implementations based on version but it’s possible to override version check by having:
sql_flags:
- mysql_alternative_upsert
inside config file.
unix_socket
listener optionWhen defining a listener, the port
option can be a port number or a string in form "unix:/path/to/socket"
to create and listen on a unix domain socket /path/to/socket
.
The new unix_socket
listener option allows to customize some options of that unix socket file.
The configurable options are:
mode
: which should be an octalowner
: which should be an integergroup
: which should be an integerThose values have no default: only when they are set, they are changed.
Example configuration:
listen:
-
port: "unix://tmp/asd/socket"
unix_socket:
mode: '0775'
owner: 117
group: 135
install_contrib_modules
top-level optionThe new install_contrib_modules
top-level option lets you declare a list of modules from ejabberd-contrib that will be installed automatically by ejabberd when it is being started. This option is read during ejabberd start or configuration reload.
This option is equivalent to installing the module manually with the command ejabberdctl module_install whatever
. It is useful when deploying ejabberd automatically with a configuration file that mentions a contrib module.
For example, let’s enable and configure some modules from ejabberd-contrib, and use the new option to ensure they get installed, all of this the very first time ejabberd runs. Extract from ejabberd.yml
:
...
install_contrib_modules:
- mod_statsdx
- mod_webadmin_config
modules:
mod_statsdx:
hooks: true
mod_webadmin_config: {}
...
The ejabberd.log file will show something like:
2023-09-25 15:32:40.282446+02:00 [info] Loading configuration from _build/relive/conf/ejabberd.yml
Module mod_statsdx has been installed and started.
The mod_statsdx configuration in your ejabberd.yml is used.
Module mod_webadmin_config has been installed and started.
The mod_webadmin_config configuration in your ejabberd.yml is used.
2023-09-25 15:32:42.201199+02:00 [info] Configuration loaded successfully
...
2023-09-25 15:32:43.163099+02:00 [info] ejabberd 23.04.115 is started in the node ejabberd@localhost in 3.15s
2023-09-25 15:32:47.069875+02:00 [info] Reloading configuration from _build/relive/conf/ejabberd.yml
2023-09-25 15:32:47.100917+02:00 [info] Configuration reloaded successfully
notify_on
option in mod_push
mod_push
has a new option: notify_on
, which possible values:
all
: generate a notification on any kind of XMPP stanzas. This is the default value.messages
: notifications are only triggered for actual chat messages with a body text (or some encrypted payload).A nick can be registered in the MUC service since ejabberd 13.06, this prevents anybody else to use that nick in any room of that MUC service.
Now ejabberd gets support to register a nick in a room, as described in XEP-0045 section 7.10 Registering with a Room
Registering a nick in the MUC service or in a room is mutually exclusive:
– A nick that is registered in the service cannot be registered in any room, not even the original owner can register it.
– Similarly, a nick registered in any room cannot be registered in the service.
allow_private_messages
converted to allowpm
Until ejabberd 23.04, MUC rooms had a configurable option called allow_private_messages
with possible values true
or false
.
Since ejabberd 23.10, that option is converted into allowpm
, with possible values:
anyone
: equivalent to allow_private_messages=true
none
: equivalent to allow_private_messages=false
participants
moderators
gen_mod
API to simplify hooks and IQ handlers registrationIf you wrote some ejabberd module, you may want to update your module to the simplified gen_mod
API. This is not mandatory, because the old way to do this is supported.
Until now, erlang modules that implemented ejabberd’s gen_mod
behaviour called ejabberd_hooks:add
and gen_iq_handler:add_iq_handler
in ther start
functions. Similarly, in their stop
function they called ejabberd_hooks:delete
and gen_iq_hanlder:remove_iq_handler
.
Since ejabberd 23.10, there is an alternative way to do this: let your start
function return {ok, List}
, where List
is a list of iq handlers and hooks that you want your module to register to. No need to unregister them in your stop
function!
How to change your module to the new API? See the changes done in mod_adhoc.erl
in commit 60002fc.
To use the Microsoft SQL Server database, the libtdsodbc
library is required, as explained in the corresponding section of the ejabberd Docs: Configuration > Databases > Microsoft SQL Server
Since this release, the ejabberd
container image includes this library.
Please notice if you install ejabberd using the binary installers and want to use MS SQL: you must install the libtdsodbc
libraries on your machine. It cannot be included in the ejabberd installer due to the nature of the odbc drivers being dynamic depending on the respective odbc backend in use.
This ejabberd release requires Erlang/OTP 20.0 or newer to compile and run, support for Erlang/OTP 19.3 is deprecated. If you are still using Erlang/OTP 19.3, please update to a more recent Erlang version. For example, the ejabberd binary installers and container images are using Erlang/OTP 26.1. That requirement increase was announced almost a year ago, check more details in the ejabberd 22.10 release announcement.
If you are still using Erlang/OTP 19.3 and cannot update it right now, there’s still a possibility to compile ejabberd 23.10 with Erlang/OTP 19.3, but please notice: there is no guarantee or support that it will compile or run correctly. If interested, revert the changed line in the file configure.ac
done in commit d299b97 and recompile.
We would like to thank the contributions to the source code, documentation, and translation provided for this release by:
mod_push
new notify_on
optionAnd also to all the people contributing in the ejabberd chatroom, issue tracker…
Customers of the ejabberd Business Edition, in addition to all those improvements and bugfixes, also get:
mod_push_logger
module to log push related eventsmod_matrix_gw_s2s
max_concurrent_connections
option to webhookThis is a more detailed list of changes in this ejabberd release:
rebar.config
rebar.config
to organize its contentre:mp()
is not an exported type--app
ERLANG_NODE=ejabberd@localhost
ejabberdctl
: Pass ERLANG_OPTS
when calling erl
to parse the INET_DIST_INTERFACE
(#4066create_room_with_opts
: Fix typo and move examples to args_example
(#4080)etop
: Let ejabberdctl etop
work in a release (if observer
application is available)get_roster
: Command now returns groups in a list instead of newlines (#4088)halt
: New command to halt ejabberd abruptly with an error status codeejabberdctl
: Fix calling ejabberdctl command with wrong number of arguments with Erlang 26ejabberdctl
: Improve printing lists in resultsejabberdctl
: Support policy=user
in the help and return proper argumentsejabberdctl
: Document how to stop a debug shell: control+gejabberdctl
: Support policy=user in the help and return proper argumentsejabberdctl
: Improve printing lists in resultsMETHOD=package
CTL_ON_
fails during ejabberd startupauth_external_user_exists_check
: New option (#3377)gen_mod
: Extend gen_mod
API to simplify hooks and IQ handlers registrationgen_mod
: Add shorter forms for gen_mod
hook/iq_handler
APIgen_mod
: Update modules to the new gen_mod
APIinstall_contrib_modules
: New option to define contrib modules to install automaticallyunix_socket
: New listener option, useful when setting unix socket files (#4059)ejabberd_systemd
: Add a few debug messagesejabberd_systemd
: Avoid using gen_server
timeout (#4054)(#4058)ejabberd_listener
: Increase default listen queue backlog value to 128, which is the default value on both Linux and FreeBSD (#4025)badpass
error messageuser_send_packet
(#3990)AddJID
textbox to top (#4067)policy=user
commands have host
instead of server
arg in docsmake-binaries
)mod_muc_log
: Add trailing backslash to URLs shown in disco infomod_muc_occupantid
: New module with support for XEP-0421 Occupant Id (#3397)mod_muc_rtbl
: Better error handling in (#4050)mod_private
: Add support for XEP-0402 PEP Native Bookmarksmod_privilege
: Don’t fail to edit roster (#3942)mod_pubsub
: Fix usage of plugins
option, which produced default_node_config
ignore (#4070)mod_pubsub
: Add pubsub_delete_item
hookmod_pubsub
: Report support of config-node-max
in pepmod_pubsub
: Relay pubsub iq queries to muc members without using bare jid (#4093)mod_pubsub
: Allow pubsub node owner to overwrite items published by other personsmod_push_keepalive
: Delay wake_on_start
mod_push_keepalive
: Don’t let hook crashmod_push
: Add notify_on
optionmod_push
: Set last-message-sender
to bare JIDmod_register_web
: Make redirect to page that end with /
(#3177)mod_shared_roster_ldap
: Don’t crash in get_member_jid
on empty output (#3614)allow_private_message
MUC room option to allowpm
(#3736)roomconfig_changesubject
in disco#info (#4085)muc_filter_message
(#4083)muc_filter_message
(#3397)muc_filter_message
too (#3397)muc_filter_message
when sending subject (#3397)ejabberd_auth_sql
: Reset scram fields when setting plain passwordmod_privacy_sql
: Fix return values from calculate_diff
mod_privacy_sql
: Optimize set_list
mod_privacy_sql
: Use more efficient way to calculate changes in set_privacy_list
https://github.com/processone/ejabberd/compare/23.04…23.10
As usual, the release is tagged in the Git source code repository on GitHub.
The source package and installers are available in ejabberd Downloads page. To check the *.asc
signature files, see How to verify ProcessOne downloads integrity.
For convenience, there are alternative download locations like the ejabberd DEB/RPM Packages Repository and the GitHub Release / Tags.
The ecs
container image is available in docker.io/ejabberd/ecs and ghcr.io/processone/ecs. The alternative ejabberd
container image is available in ghcr.io/processone/ejabberd.
If you consider that you’ve found a bug, please search or fill a bug report on GitHub Issues.
The post ejabberd 23.10 first appeared on ProcessOne.]]>mod_mam
support for XEP-0425: Message Moderationmod_muc_rtbl
, Real-Time Block List for MUC roomsA more detailed explanation of these topics and other features:
There are many improvements in the area of SQL databases (see #3980 and #3982):
new
schema and the corresponding schema migration, along with other minor improvements and bugfixes.Please upgrade your existing SQL database, check the notes later in this document!
mod_mam
support for XEP-0425: Message ModerationXEP-0425: Message Moderation allows a Multi-User Chat (XEP-0045) moderator to moderate certain group chat messages, for example by removing them from the group chat history, as part of an effort to address and resolve issues such as message spam, inappropriate venue language, or revealing private personal information of others. It also allows moderators to correct a message on another user’s behalf, or flag a message as inappropriate, without having to retract it.
Clients that currently support this XEP are Gajim, Converse.js, Monocles, and have read-only support Poezio and XMPP Web.
mod_muc_rtbl
moduleThis new module implements Real-Time Block List for MUC rooms. It works by monitoring remote pubsub nodes according to the specification described in xmppbl.org.
captcha_url
option now accepts auto
valueIn recent ejabberd releases, captcha_cmd got support for macros (in ejabberd 22.10) and support for using modules (in ejabberd 23.01).
Now captcha_url gets an improvement: if set to auto
, it tries to detect the URL automatically, taking into account the ejabberd configuration. This is now the default. This should be good enough in most cases, but manually setting the URL may be necessary when using port forwarding or very specific setups.
This is the last ejabberd release with support for Erlang/OTP 19.3. If you have not already done so, please upgrade to Erlang/OTP 20.0 or newer before the next ejabberd release. See the ejabberd 22.10 release announcement for more details.
About the binary packages provided for ejabberd:
mix
, ecs
and ejabberd
container images now use Alpine 3.17.ejabberd
container image now supports an alternative build method, useful to work around a problem in QEMU and Erlang 25 when building the image for the arm64
architecture.ecs
container imageThe ecs
container image is built using the files from docker-ejabberd/ecs and published in docker.io/ejabberd/ecs. This image generally gets only minimal fixes, no major or breaking changes, but in this release it got one change that requires administrator intervention.
The Erlang node name is now fixed to ejabberd@localhost
by default, instead of being variable based on the container hostname. If you previously allowed ejabberd to choose its node name (which was random), it will now create a new mnesia database instead of using the previous one:
$ docker exec -it ejabberd ls /home/ejabberd/database/
ejabberd@1ca968a0301a
ejabberd@localhost
...
A simple solution is to create a container that provides ERLANG_NODE_ARG
with the old erlang node name, for example:
docker run ... -e ERLANG_NODE_ARG=ejabberd@1ca968a0301a
or in docker-compose.yml
version: '3.7'
services:
main:
image: ejabberd/ecs
environment:
- ERLANG_NODE_ARG=ejabberd@1ca968a0301a
Another solution is to change the mnesia node name in the mnesia spool files.
ecs
container imageIn addition to the previously mentioned change to the default erlang node name, the ecs
container image has received other improvements:
ecs
and mix
container images, those images are uploaded as artifacts and are available for download in the corresponding runs.ecs
README file: Clustering and Clustering Example.In addition to the usual improvements and fixes, some sections of the ejabberd documentation have been improved:
We would like to thank the following people for their contributions to the source code, documentation, and translation for this release:
And also to all the people who help solve doubts and problems in the ejabberd chatroom and issue tracker.
These notes allow you to apply the SQL database schema improvements in this ejabberd release to your existing SQL database. Please consider which database you are using and whether it is the default or the new schema.
Fixes a long-standing bug in the new schema on PostgreSQL. The fix for all existing affected installations is the same:
ALTER TABLE vcard_search DROP CONSTRAINT vcard_search_pkey;
ALTER TABLE vcard_search ADD PRIMARY KEY (server_host, lusername);
To convert columns to allow up to 2 billion rows in these tables. This conversion requires full table rebuilds and will take a long time if the tables already have many rows. Optional: This is not necessary if the tables will never grow large.
ALTER TABLE archive ALTER COLUMN id TYPE BIGINT;
ALTER TABLE privacy_list ALTER COLUMN id TYPE BIGINT;
ALTER TABLE pubsub_node ALTER COLUMN nodeid TYPE BIGINT;
ALTER TABLE pubsub_state ALTER COLUMN stateid TYPE BIGINT;
ALTER TABLE spool ALTER COLUMN seq TYPE BIGINT;
DROP INDEX i_rosteru_username;
DROP INDEX i_sr_user_jid;
DROP INDEX i_privacy_list_username;
DROP INDEX i_private_storage_username;
DROP INDEX i_muc_online_users_us;
DROP INDEX i_route_domain;
DROP INDEX i_mix_participant_chan_serv;
DROP INDEX i_mix_subscription_chan_serv_ud;
DROP INDEX i_mix_subscription_chan_serv;
DROP INDEX i_mix_pam_us;
DROP INDEX i_rosteru_sh_username;
DROP INDEX i_sr_user_sh_jid;
DROP INDEX i_privacy_list_sh_username;
DROP INDEX i_private_storage_sh_username;
DROP INDEX i_muc_online_users_us;
DROP INDEX i_route_domain;
DROP INDEX i_mix_participant_chan_serv;
DROP INDEX i_mix_subscription_chan_serv_ud;
DROP INDEX i_mix_subscription_chan_serv;
DROP INDEX i_mix_pam_us;
And now add index that might be missing
In PostgreSQL:
CREATE INDEX i_push_session_sh_username_timestamp ON push_session USING btree (server_host, username, timestamp);
In SQLite:
CREATE INDEX i_push_session_sh_username_timestamp ON push_session (server_host, username, timestamp);
ALTER TABLE rosterusers DROP INDEX i_rosteru_username;
ALTER TABLE sr_user DROP INDEX i_sr_user_jid;
ALTER TABLE privacy_list DROP INDEX i_privacy_list_username;
ALTER TABLE private_storage DROP INDEX i_private_storage_username;
ALTER TABLE muc_online_users DROP INDEX i_muc_online_users_us;
ALTER TABLE route DROP INDEX i_route_domain;
ALTER TABLE mix_participant DROP INDEX i_mix_participant_chan_serv;
ALTER TABLE mix_participant DROP INDEX i_mix_subscription_chan_serv_ud;
ALTER TABLE mix_participant DROP INDEX i_mix_subscription_chan_serv;
ALTER TABLE mix_pam DROP INDEX i_mix_pam_u;
ALTER TABLE rosterusers DROP INDEX i_rosteru_sh_username;
ALTER TABLE sr_user DROP INDEX i_sr_user_sh_jid;
ALTER TABLE privacy_list DROP INDEX i_privacy_list_sh_username;
ALTER TABLE private_storage DROP INDEX i_private_storage_sh_username;
ALTER TABLE muc_online_users DROP INDEX i_muc_online_users_us;
ALTER TABLE route DROP INDEX i_route_domain;
ALTER TABLE mix_participant DROP INDEX i_mix_participant_chan_serv;
ALTER TABLE mix_participant DROP INDEX i_mix_subscription_chan_serv_ud;
ALTER TABLE mix_participant DROP INDEX i_mix_subscription_chan_serv;
ALTER TABLE mix_pam DROP INDEX i_mix_pam_us;
Add index that might be missing:
CREATE INDEX i_push_session_sh_username_timestamp ON push_session (server_host, username(191), timestamp);
DROP INDEX [rosterusers_username] ON [rosterusers];
DROP INDEX [sr_user_jid] ON [sr_user];
DROP INDEX [privacy_list_username] ON [privacy_list];
DROP INDEX [private_storage_username] ON [private_storage];
DROP INDEX [muc_online_users_us] ON [muc_online_users];
DROP INDEX [route_domain] ON [route];
go
MS SQL schema was missing some tables added in earlier versions of ejabberd:
CREATE TABLE [dbo].[mix_channel] (
[channel] [varchar] (250) NOT NULL,
[service] [varchar] (250) NOT NULL,
[username] [varchar] (250) NOT NULL,
[domain] [varchar] (250) NOT NULL,
[jid] [varchar] (250) NOT NULL,
[hidden] [smallint] NOT NULL,
[hmac_key] [text] NOT NULL,
[created_at] [datetime] NOT NULL DEFAULT GETDATE()
) TEXTIMAGE_ON [PRIMARY];
CREATE UNIQUE CLUSTERED INDEX [mix_channel] ON [mix_channel] (channel, service)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE INDEX [mix_channel_serv] ON [mix_channel] (service)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE TABLE [dbo].[mix_participant] (
[channel] [varchar] (250) NOT NULL,
[service] [varchar] (250) NOT NULL,
[username] [varchar] (250) NOT NULL,
[domain] [varchar] (250) NOT NULL,
[jid] [varchar] (250) NOT NULL,
[id] [text] NOT NULL,
[nick] [text] NOT NULL,
[created_at] [datetime] NOT NULL DEFAULT GETDATE()
) TEXTIMAGE_ON [PRIMARY];
CREATE UNIQUE INDEX [mix_participant] ON [mix_participant] (channel, service, username, domain)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE INDEX [mix_participant_chan_serv] ON [mix_participant] (channel, service)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE TABLE [dbo].[mix_subscription] (
[channel] [varchar] (250) NOT NULL,
[service] [varchar] (250) NOT NULL,
[username] [varchar] (250) NOT NULL,
[domain] [varchar] (250) NOT NULL,
[node] [varchar] (250) NOT NULL,
[jid] [varchar] (250) NOT NULL
);
CREATE UNIQUE INDEX [mix_subscription] ON [mix_subscription] (channel, service, username, domain, node)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE INDEX [mix_subscription_chan_serv_ud] ON [mix_subscription] (channel, service, username, domain)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE INDEX [mix_subscription_chan_serv_node] ON [mix_subscription] (channel, service, node)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE INDEX [mix_subscription_chan_serv] ON [mix_subscription] (channel, service)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE TABLE [dbo].[mix_pam] (
[username] [varchar] (250) NOT NULL,
[channel] [varchar] (250) NOT NULL,
[service] [varchar] (250) NOT NULL,
[id] [text] NOT NULL,
[created_at] [datetime] NOT NULL DEFAULT GETDATE()
) TEXTIMAGE_ON [PRIMARY];
CREATE UNIQUE CLUSTERED INDEX [mix_pam] ON [mix_pam] (username, channel, service)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
go
MS SQL also had some incompatible column types:
ALTER TABLE [dbo].[muc_online_room] ALTER COLUMN [node] VARCHAR (250);
ALTER TABLE [dbo].[muc_online_room] ALTER COLUMN [pid] VARCHAR (100);
ALTER TABLE [dbo].[muc_online_users] ALTER COLUMN [node] VARCHAR (250);
ALTER TABLE [dbo].[pubsub_node_option] ALTER COLUMN [name] VARCHAR (250);
ALTER TABLE [dbo].[pubsub_node_option] ALTER COLUMN [val] VARCHAR (250);
ALTER TABLE [dbo].[pubsub_node] ALTER COLUMN [plugin] VARCHAR (32);
go
… and mqtt_pub
table was incorrectly defined in old schema:
ALTER TABLE [dbo].[mqtt_pub] DROP CONSTRAINT [i_mqtt_topic_server];
ALTER TABLE [dbo].[mqtt_pub] DROP COLUMN [server_host];
ALTER TABLE [dbo].[mqtt_pub] ALTER COLUMN [resource] VARCHAR (250);
ALTER TABLE [dbo].[mqtt_pub] ALTER COLUMN [topic] VARCHAR (250);
ALTER TABLE [dbo].[mqtt_pub] ALTER COLUMN [username] VARCHAR (250);
CREATE UNIQUE CLUSTERED INDEX [dbo].[mqtt_topic] ON [mqtt_pub] (topic)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
go
… and sr_group
index/PK was inconsistent with other DBs:
ALTER TABLE [dbo].[sr_group] DROP CONSTRAINT [sr_group_PRIMARY];
CREATE UNIQUE CLUSTERED INDEX [sr_group_name] ON [sr_group] ([name])
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
go
s2s_out_bounce_packet
hookejabberd_system_monitor
before stopping nodecaptcha_url
option now accepts auto
value, and it’s the defaultmod_mam
: Add support for XEP-0425: Message Moderationmod_mam_sql
: Fix problem with results of mam queries using rsm with max and beforemod_muc_rtbl
: New module for Real-Time Block List for MUC rooms (#4017)mod_roster
: Set roster name from XEP-0172, or the stored one (#1611)mod_roster
: Preliminary support to store extra elements in subscription request (#840)mod_pubsub
: Pubsub xdata fields max_item/item_expira/children_max
use max
not infinity
mod_vcard_xupdate
: Invalidate vcard_xupdate
cache on all nodes when vcard is updatedext_mod
: Improve support for loading *.so
files from ext_mod
dependenciesgen_html_doc_for_commands
commandget_room_history
none
role for outcastsmod_muc_room:set_opts
process persistent flag firstcreate_room_with_opts
commandmod_muc:create_room()
update_sql
command: Many improvements in new schema migrationupdate_sql
command: Add support to migrate MySQL toonew
schema for MS SQLORDER BY
in subqueryejabberd_config:set_option/2
extauth.py
for testsextauth.py
ecs
container imagetini
as runtime initERLANG_NODE
fixed to ejabberd@localhost
ejabberd
container imageMETHOD
to build container using packages (#3983)tini
as runtime init/opt/ejabberd-*/lib
like the installersHOME
volume, it contains all the required subdirs.../releases/COOKIE
, it’s no longer includedhttps://github.com/processone/ejabberd/compare/23.01…23.04
As usual, the release is tagged in the git source repository on GitHub.
The source package and installers are available on the ejabberd Downloads page. To verify the *.asc
signature files, see How to verify the integrity of ProcessOne downloads.
For convenience, there are alternative download locations such as the ejabberd DEB/RPM Packages Repository and the GitHub Release / Tags.
The ecs
container image is available in docker.io/ejabberd/ecs and ghcr.io/processone/ecs. The alternative ejabberd
container image is available in ghcr.io/processone/ejabberd.
If you think you’ve found a bug, please search or file a bug report at GitHub Issues.
The post ejabberd 23.04 first appeared on ProcessOne.]]>Almost three months after the previous release, ejabberd 23.01 includes many bug fixes, several improvements and some new features.
A new module, mod_mqtt_bridge
, can be used to replicate changes to MQTT topics between local and remote servers.
A more detailed explanation of those topics and other features:
Remember that support for Erlang/OTP 19.3 is discouraged, and will be removed in a future release. Please upgrade to Erlang/OTP 20.0 or newer. Check more details in the ejabberd 22.10 release announcement.
This new module allows synchronizing topic changes between local and remote servers. It can be configured to replicate local changes to remote server, or can subscribe to topics on remote server and update local copies when they change.
When connecting to a remote server you can use native or websocket encapsulated protocol, and you can connect using both v4 and v5 protocol. It can authenticate using username/password pairs or with client TLS certificates.
Regarding MQTT support, there are several new hooks:
mqtt_publish
: New hook for MQTT publish eventmqtt_subscribe
and mqtt_unsubscribe
: New hooks for MQTT subscribe & unsubscribe eventslog_modules_fully
The loglevel
top-level option specifies the verbosity of log files generated by ejabberd.
If you want some specific modules to log everything, independently from whatever value you have configured in loglevel
, now you can use the new log_modules_fully
option.
For example, if you are investigating some problem in ejabberd_sm
and mod_client_state
:
loglevel: warning
log_modules_fully: [ejabberd_sm, mod_client_state]
(This option works only on systems with Erlang 22 or newer).
outgoing_s2s_families
The outgoing_s2s_families
top-level option specifies which address families to try, in what order.
The default value has now been changed to try IPv6 first, as servers are within data centers where IPv6 is more commonly enabled (contrary to clients). And if it’s not present, then it’ll just fall back to IPv4.
By the way, this option is obsolete and irrelevant when using ejabberd 23.01 and Erlang/OTP 22, or newer versions of them.
captcha_cmd
The captcha_cmd
top-level option specifies the full path to a script that can generate a CAPTCHA image. Now this option may specify an Erlang module name, which should implement a function to generate a CAPTCHA image.
ejabberd does not include any such module, but there are two available in the ejabberd-contrib repository that you can install and try: mod_ecaptcha
and mod_captcha_rust
.
The protocols implemented or supported by ejabberd are defined in the corresponding source code modules since ejabberd 15.06. Until now, only the XEP number and supported version were tracked. Since now, it’s possible to document what ejabberd version first implemented it, the implementation status and an arbitrary comment.
That information until now was only used by the script tools/check_xep_versions.sh
. A new script is added, tools/generate-doap.sh
, to generate a DOAP file with that information. A new target is added to Makefile: make doap
.
And that DOAP file is now published as ejabberd.doap
in the git repository. That file is read by the XMPP.org website to show ejabberd’s protocols, see XMPP Servers: ejabberd.
Support for Visual Studio Code and variants is vastly improved. Thanks to the Erlang LS VSCode extension, the ejabberd git repository includes support for developing, compiling and debugging ejabberd with Visual Studio Code, VSCodium, Coder’s code-server and Github Codespaces.
See more details in the ejabberd Docs: VSCode page.
misc:uri_parse/2
to allow declaring default ports for protocolsmnesia_system_event mnesia_up
when other node joins this (#3842)mod_conversejs
to CDN when there is no local copiespriv
hibernation_time
is not an option worth storing in room state (#3946)multicastc
was cached (#3950)ssl
options to mysql driverstandard_conforming_strings
to off
(#3944)jid
as a HTTP URL query argumentpubsub#type
field in disco#info
query to the node (#3914)api_permissions
: Fix option crash when doesn’t have who:
sectionlog_modules_fully
: New option to list modules that will log everythingoutgoing_s2s_families
: Changed option’s default to IPv6, and fall back to IPv4members_only
roomssplit/2
function that works with Erlang/OTP as old as 19.3cacerts
in SQL connections-protocol
erlang attributetools/generate-doap.sh
: New script to generate DOAP file, add make doap
(#3915)ejabberd.doap
: New DOAP file describing ejabberd supported protocolsmqtt_publish
: New hook for MQTT publish eventmqtt_(un)subscribe
: New hooks for MQTT subscribe & unsubscribe events.devcontainer
to use use devcontainer image and .vscode
.vscode
files to instruct VSCode how to run ejabberdhttps://github.com/processone/ejabberd/compare/22.10…23.01
As usual, the release is tagged in the Git source code repository on GitHub.
The source package and installers are available in ejabberd Downloads page. To check the *.asc
signature files, see How to verify ProcessOne downloads integrity.
For convenience, there are alternative download locations like the ejabberd DEB/RPM Packages Repository and the GitHub Release / Tags.
The Docker image is in Docker Hub, and there’s an alternative Container image in GitHub Packages.
If you suspect that you’ve found a bug, please search or fill a bug report on GitHub Issues.
The post ejabberd 23.01 first appeared on ProcessOne.]]>
ejabberd is a scalable messaging server. That sums it all and that does not do justice to this critical piece of the Internet infrastructure. Sure, it is known to be the most scalable XMPP server, so scalable that it was used as a building brick to build Whatsapp messaging service. This is something that we have always been proud of, something you can easily brag about when meeting your friends.
But is that just it? Of course not. Today, with the troubles at Twitter, something appeared clearly.
ejabberd is important because it helped build much more than Whatsapp or any other big name high-profile projects we have built. It is important because it makes people communicate, in a federated way. It is important because it implements open protocols, and now several of them: XMPP, MQTT, SIP and now Matrix.
ejabberd is about federation. It is helping people on different servers, domains, companies, communities or even countries chatting together. And today even more than 20 years ago, it really matters. We have built ejabberd for 20 years, because it is a critical building brick of what makes the Internet exists. Openness, interoperability, federation. It is one of the few software that prosper outside of the spotlights and make the Internet what it is, along for example with web and mail servers.
This is something we are pondering as we are thinking about the next steps, the next 20 years. But deep down, we know for sure, what we are about. ejabberd is about federation. You will read more from us here soon. It is a tradition. No birthday celebration speech is complete without looking back at the past.
It is hard to track all ejabberd usage, but we know that ejabberd empowers more than a billion users. Not bad for a piece of code we wrote. Trillions of messages went through our lines of code.
As mentioned in this post ten years ago:
Closed protocols come and go – ejabberd and XMPP remains
Happy 20th birthday, ejabberd!
The very first public commit in ejabberd’s source code was done by Alexey Shchepin the 16th November of 2002. That was in the Jabber.ru CVS server. Later when that machine had technical problems, the development code moved to JabberStudio CVS.
The first official ejabberd release was ejabberd 0.5 in November 2003. The ejabberd home page at that time was a simple HTML. It’s also worth checking the early stage of the Ejabberd Installation and Operation Guide. Notice this first ejabberd logo represented a frog-like animal sitting on a “Jabber globe bulb,” with bat wings, dangerous-looking cogs, and an Erlang suit.
After the 0.7.5 release in October 2004, ejabberd home page moved from JabberStudio to ejabberd.jabber.ru, and the bug tracker to Jabber.ru’s Bugzilla. For this Drupal site, the logo changed to a hedgehog, and that would remain ten years until the final website and logo update in 2015.
At the beginning of 2005, JabberStudio CVS had technical difficulties and the development code moved to ProcessOne SVN. Notice that ProcessOne contracted Alexey to work in J-EAI, a project based in ejabberd specially designed for some business usages, and later extended that relationship to ejabberd.
In February 2005 the source code repository moved from SVN to Git, and the bug tracker to JIRA. Around October 2010 the source code repository and the bug tracker were finally moved to GitHub.
From around that time, there’s an interview to Alexey Shchepin which covers the initial concept and years of ejabberd development. By the way, there was another interview two years ago.
The ejabberd code base got a relevant massive change with the data binarization (use Erlang binaries instead of Erlang strings for data representation) in March 2013, which jumped ejabberd version from 2.1.12 to 13.03.
The next years followed another major source code change: the movement of many C/C++ code to independent external libraries.
Today, ejabberd is not just about XMPP. Even if it is mostly know for its great XMPP support, it also supports several other protocols:
– SIP support to connect SIP phone was added in 2014 (see ejabberd 14.05)
– Support for MQTT protocol, to better support Internet of Things use cases, was added initially in the Business Edition, and some months later added to the Community Server 19.02.
– Right now Matrix federation is being introduced to allow interop between ejabberd and Matrix servers, to ejabberd Business Edition internally or on Fluux ejabberd SaaS platform. It will come later to ejabberd Community Server.
ejabberd keeps on improving at a steady pace and is happy to open to other protocols and communities.
The oldest unchanged function in ejabberd is probably one of the least used: stop/0. And the oldest functional line is the SETS macro definition.
The ejabberd repository got 1,070,325 line insertions and 901,287 line deletions. When counting both ejabberd and the dependency libraries, they got 1.693.180 line insertions and 1.108.873 line deletions. With all this, the ejabberd source code went from 13 files to 868, from 1,448 lines of code to 480,961.
Looking at the programming languages of ejabberd and its libraries, Erlang is obviously the major one, and C comes as a relevant second:
Language | Files | Lines | Code | Comments | Blanks |
---|---|---|---|---|---|
ABNF | 3 | 128 | 110 | 3 | 15 |
ASN.1 | 1 | 14 | 10 | 0 | 4 |
Autoconf | 14 | 696 | 544 | 33 | 119 |
Batch | 4 | 31 | 21 | 0 | 10 |
C | 20 | 187549 | 139764 | 39614 | 8171 |
C Header | 8 | 11199 | 3783 | 6979 | 437 |
C++ | 1 | 533 | 442 | 17 | 74 |
CSS | 5 | 532 | 507 | 0 | 25 |
Elixir | 32 | 1888 | 1469 | 130 | 289 |
Erlang | 604 | 247901 | 208912 | 18368 | 20621 |
JavaScript | 2 | 23 | 21 | 1 | 1 |
Lua | 1 | 16 | 16 | 0 | 0 |
Makefile | 21 | 848 | 635 | 10 | 203 |
Perl | 3 | 1086 | 897 | 63 | 126 |
Python | 1 | 53 | 49 | 0 | 4 |
RPM Specfile | 3 | 5408 | 3928 | 1059 | 421 |
Shell | 21 | 4031 | 3263 | 336 | 432 |
SQL | 9 | 3857 | 2994 | 303 | 560 |
TCL | 3 | 1179 | 1002 | 69 | 108 |
Plain Text | 13 | 1870 | 0 | 1561 | 309 |
YAML | 20 | 1448 | 1357 | 53 | 38 |