Skip to content

Instantly share code, notes, and snippets.

@gabrielgatu
Last active September 21, 2016 10:49
Show Gist options
  • Save gabrielgatu/5ecd52884ded46902c49a0affcd6b1e6 to your computer and use it in GitHub Desktop.
Save gabrielgatu/5ecd52884ded46902c49a0affcd6b1e6 to your computer and use it in GitHub Desktop.
Configuration proposal

Elixir based config for ejabberd

Goals

Through the mix tools and the git based modules (see below) the goal is to allow ejabberd users to access more easily the configuration part of their project and allow them to visualize the modules used and easily add new ones from external sources.

Elixir based config file

Location

The configuration file can be located anywhere. However it is highly suggested to place it into the /config folder. (ex: config/ejabberd.exs)

Naming

The configuration is specified in a module which, by convention, is named ProjectName.Ejabberd.ConfigFile.

At the top of the module should be placed the use Ejabberd.Config macro, that takes care of importing the functions and macros necessary.

Usage

To use the elixir based configuration, instead of the default yaml one, it is necessary to do a small change:

  • When using ejabberd from mix or source (Always necessary):

    • Change inside /config/config.exs the line ejabberd.yml into ejabberd.exs. Copy ejabberd.exs into /config.
  • When using ejabberd built from source:

    • Change inside /etc/ejabberd/ejabberdctl.cfg the line EJABBERD_CONFIG_PATH into $ETC_DIR/ejabberd.exs. Copy ejabberd.exs into /config.

Structure

Start/0

Function called initially. Should return a keyword list containing all the general configurations for ejabberd.

Options: log levels, hosts, shaper, acl, access

Example
def start do
  [loglevel: 4,
   log_rotate_size: 10485760,
   log_rotate_date: "",
   log_rotate_count: 1,
   log_rate_limit: 100,
   auth_method: :internal,
   max_fsm_queue: 1000,
   language: "en",
   allow_contrib_modules: true,
   hosts: ["localhost"],
   shaper: shaper,
   acl: acl,
   access: access]
end

defp shaper do
  [normal: 1000,
    fast: 50000,
    max_fsm_queue: 1000]
end

defp acl do
  [local:
    [user_regexp: "", loopback: [ip: "127.0.0.0/8"]]]
end

defp access do
  [max_user_sessions: [all: 10],
   max_user_offline_messages: [admin: 5000, all: 100],
   local: [local: :allow],
   c2s: [blocked: :deny, all: :allow],
   c2s_shaper: [admin: :none, all: :normal],
   s2s_shaper: [all: :fast],
   announce: [admin: :allow],
   configure: [admin: :allow],
   muc_admin: [admin: :allow],
   muc_create: [local: :allow],
   muc: [all: :allow],
   pubsub_createnode: [local: :allow],
   register: [all: :allow],
   trusted_network: [loopback: :allow]]
end

Modules configuration

Modules registration and configuration happens thorugh the macros imported by the Ejabberd.Config module, which are:

  • listen: Configuration for listening ports
  • module: Configuration for modules

The structure of this macros are very similar to the module definition in elixir and is based on the use of annotations (module attributes):

listen :ejabberd_c2s do
  @opts [port: 5222, max_stanza_size: 65536, shaper: :c2s_shaper, access: :c2s]
end
module ModOffline do
  @opts [access_max_user_messages: :max_user_offline_messages]
end

Git based modules

When registering a module inside the configuration file it is supposed to be already present on the local machine.

However, it is possible to register a remote module by specifying the git url where it can be found, trough the @git annotation. In this case, the system, during compilation time, will download the module sources and place it inside ~/.ejabberd-modules/sources/ejabberd-contrib, then install them thorugh the ejabberd installer.

So, modules fetched in this way must have the structure of:

  • /mod_name
    • ./mod_name.spec
    • ./lib/

An ambitious project could be to create an official ejabberd repository, with all the modules, a sort of marketplace, where users can register and publish free/premium modules (and annotations could be supported to register a license key for that module used during fetching).

Example
module ModPresenceHello do
  @git "https://github.com/gabrielgatu/mod_presence_hello"
  @name "mod_presence_hello" # Not necessary in this case because inferred correctly from the git url
end

Annotations

Annotations are used to specify specific configurations for a module. The advantage of using them is that they are highly extensible (can support any value) and new ones can easily be supported in the future.

Another great advantage is that they are already used in elixir for the same purpose, so the ejabberd configuration will be consistent with the language features.

Supported:

  • @active: If false, then the module will be ignored by ejabberd
  • @opts: Specifies the config options
  • @git: Specifies the git repo where the module sources can be found
  • @name: For git modules. Specifies the name of the folder. If missing, it is inferred from the git url.
  • @dependency: Allows to specify a collection with all the modules it depends on (below more details)
Example
module ModPresenceHello do
  @active false # Currently disabled
  @git "https://github.com/gabrielgatu/mod_presence_hello" # Fetched inside ~/.ejabberd-modules/sources/ejabberd-contrib
  @name "mod_presence_hello" # Not necessary, because inferred from @git
  @dependency [:mod_version, :mod_time] # If one of them is missing, a warning is logged
  @opts [admin: false] # @opts Should be always the last annotation, by convention
end

@Dependency

If a module depends on other modules, this dependency could be specified thorugh the @dependency annotation. This, however, will not affect the modules startup order, which will still be in the order they are specified, but it will allow the user to know if a module doesn't have all it's dependency configured, by logging a warning.

Example
module :mod_privacy do
end

module :mod_blocking do
  @dependency [:mod_privacy]
end

Hooks

It is possible to specify also hooks inside the config file, thorugh the hook/3 macro. The 3 parameters are:

  • 1 - Event name

  • 2 - Optional parameters. Available: :host and :priority. If missing, defaults are used. (host: :global | priority: 50)

  • 3 - Callback. Could be an anonymous function or a callback from a module, use the &ModuleName.function/arity format for that.

Example:
hook :register_user, [host: "localhost"], fn(user, server) ->
  IO.puts "[info] User registered: #{user} on #{server}"
end

Example of configuration

Example using elixir based configuration.

Command line tools

Introduction

Some utility command line tools has been integrated with ejabberd, that allows the user to archieve actions like:

  • mix ejabberd.new: Scaffold new ejabberd based project. To install see: https://github.com/gabrielgatu/ejabberd_task

  • mix ejabberd.deps.tree: Lists all the modules, acls, etc.. used by ejabberd, contained inside the config file. (The @dependency annotation is being used to draw a tree with sub-deps)

Example of mix ejabberd.deps.tree
├── mod_adhoc
├── mod_announce
├── mod_blocking
│   └── mod_privacy
├── mod_caps
├── mod_carboncopy
├── mod_client_state
├── mod_configure
├── mod_disco
├── mod_irc
├── mod_http_bind
├── mod_last
├── mod_muc
├── mod_offline
├── mod_ping
├── mod_private
├── mod_pubsub
├── mod_register
├── mod_roster
├── mod_shared_roster
├── mod_stats
├── mod_time
├── mod_version
├── mod_version
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment