Skip to content

Selected hooks description

This is a brief documentation for a few selected hooks. Though hooks & handlers differ in what they are there to do, it is not necessary to describe them all, because the mechanism is general. The following is meant to give you the idea of how the hooks work, what they are used for and the various purposes they can serve.


mongoose_c2s_hooks:user_send_packet(HostType, Acc, Params)
mongoose_c2s_hooks:user_send_message(HostType, Acc, Params)
mongoose_c2s_hooks:user_send_presence(HostType, Acc, Params)
mongoose_c2s_hooks:user_send_iq(HostType, Acc, Params)
mongoose_c2s_hooks:user_send_xmlel(HostType, Acc, Params)
These hooks are run in mongoose_c2s after the C2S process receives an XML element from the client.

The hooks won't be called for stanzas arriving from a user served by a federated server (i.e. on a server-to-server connection handled by ejabberd_s2s).

The logic depends on the C2S state, which changes during the connection, authentication and resource binding process:

Hooks called for session_established

Some rudimentary verification of the stanza is done once it is received from the socket:

  • if present, the from attribute of the stanza is checked against the identity of the user whose session the process in question serves; if the identity does not match the contents of the attribute, an error is returned,
  • the recipient JID (to attribute) format is verified.

After successful checks, the following hooks are called. The first one is user_send_packet, which is called for all received XML elements. Next, depending on the type of the element, one of the following hooks is called:

  • user_send_message for messages,
  • user_send_presence for presences,
  • user_send_iq for IQ (info/query) stanzas,
  • user_send_xmlel for other XML elements.

These type-specific hooks should be used instead of user_send_packet when possible.

Hooks called for other states

If the session is not established (e.g. the client hasn't authenticated or its resource is not bound yet), only the user_send_xmlel hook is called regardless of the XML element type. No other user_send_* hooks are called, and no stanza checks are performed.

Handler examples

These hooks are handled by the following modules:

  • mod_blocking - handles IQ requests for blocking lists.
  • mod_caps - detects and caches capability information sent with certain presences for later use.
  • mod_carboncopy - forwards messages to all the user's resources which have carbon copying enabled.
  • mod_event_pusher - sends selected messages to an external service.
  • mod_inbox - stores messages in the user's inbox.
  • mod_mam - stores outgoing messages in an archive.
  • mod_ping - upon reception of every message from the client, this module (re)starts a timer; if nothing more is received from the client within 60 seconds, it sends an IQ ping, to which the client should reply - which starts another timer.
  • mod_presence - handles presence stanzas, updating the user presence state and broadcasting presence updates.
  • mod_privacy - filters sent stanzas according to privacy lists and handles privacy-related IQ requests.
  • mod_register - registers a new user when a registration IQ is received. user_send_xmlel is used because the stanza is received while the session is not established.
  • mod_smart_markers - checks if the stanza contains chat markers info and stores the update.
  • mod_stream_management - counts stanzas sent by the client and handles special XML elements like <a> and <enable>.

filter_packet and filter_local_packet

mongoose_hooks:filter_packet({From, To, Acc, Packet})
mongoose_hooks:filter_local_packet({From, To, Acc, Packet})

These hooks are run when the packet is being routed by ejaberd_router:route/4, which is the most general function used to route stanzas across the entire cluster. For example, mongoose_c2s calls it after calling the user_send_message or user_send_iq hook, and multiple modules use it for sending replies and errors.

  • filter_packet is run by mongoose_router_global for all routed packets. It is called at the start of the routing procedure.
  • filter_local_packet is run by mongoose_local_delivery when the packet is being routed to a domain hosted by the local server.

The handlers expect the {From, To, Acc, Packet} accumulator as their first argument. The stanza can be filtered out (in case the handler returns drop), left unchanged or modified.

filter_packet is a global hook

Note the hook code inside mongoose_hooks:

filter_packet(Acc) ->
    run_global_hook(filter_packet, Acc, #{}).
This hook is run not for a host type, but globally across the whole cluster. Keep that in mind when registering the handlers and appropriately use the atom global instead of a host type as the second argument.

Handler examples

These hooks are handled by the following modules:

  • mod_domain_isolation - filters out cross-domain stanzas.
  • mod_event_pusher - sends out configured events (e.g. push notifications) for incoming stanzas.
  • mod_inbox - stores incoming messages in the recipient's inbox.
  • mod_mam - stores incoming messages in the recipient's archive, and adds MAM-related elements to the message.
  • mod_pubsub - for each subscription authorization form sent by a node owner, the subscription state is updated, and the stanza is dropped.
  • mod_smart_markers - filters out chat markers, because they are handled separately by mod_offline_chatmarkers.


mongoose_c2s_hooks:user_receive_packet(HostType, Acc, Params)
mongoose_c2s_hooks:user_receive_message(HostType, Acc, Params)
mongoose_c2s_hooks:user_receive_presence(HostType, Acc, Params)
mongoose_c2s_hooks:user_receive_iq(HostType, Acc, Params)
mongoose_c2s_hooks:user_receive_xmlel(HostType, Acc, Params)

These hooks are run in mongoose_c2s after the recipient's C2S process receives an XML element and before sending it to the user.

The hooks won't run for stanzas which are destined to users of a different XMPP domain served by a federated server, connection to which is handled by ejabberd_s2s.

The first hook is user_receive_packet, which is called for all received XML elements. Next, depending on the type of the stanza, one of the following hooks is called:

  • user_receive_message for messages,
  • user_receive_presence for presences,
  • user_receive_iq for IQ (info/query) stanzas,
  • user_receive_xmlel for other XML elements.

These type-specific hooks should be used instead of user_receive_packet when possible.

Handler examples

These hooks are handled by the following modules:

  • mod_caps - detects and caches capability information sent with certain messages for later use.
  • mod_carboncopy - forwards messages to all the user's resources which have carbon copying enabled.
  • mod_last - filters queries for user's last activity according to presence subscriptions.
  • mod_presence - handles incoming presences from other users, updating the presence status, and responds to presence probes.
  • mod_privacy - filters received stanzas according to privacy lists.
  • mod_stream_management - filters out stanzas with conflicting session ID's.


mongoose_hooks:offline_message(Acc, From, To, Packet)

ejabberd_sm runs this hook for each message which cannot be delivered, because no resource (i.e. device or desktop client application) of its recipient is available online for delivery.

Handler examples

This hook is handled by the following modules, listed in the order of execution:

  • mod_offline_chatmarkers - for chat marker messages, the handler stores them and returns {stop, Acc}, preventing further handlers from being called.

  • mod_offline - stores messages in a persistent way until the recipient comes online, and the message can be successfully delivered. The handler returns {stop, Acc} for all messages, preventing further handlers from being called.

  • mod_offline_stub - returns {stop, Acc} for all messages, preventing further handlers from being called.

  • ejabberd_sm - calls ejabberd_sm:bounce_offline_message, which responds with the <service-unavailable/> stanza error. In the case of using mod_mam the message is actually stored, and no such error should be sent - that's why the module mod_offline_stub can be enabled.


mongoose_hooks:remove_user(Acc, LServer, LUser)

remove_user is run by ejabberd_auth - the authentication module - when a request is made to remove the user from the database of the server.

Handler examples

This hook is used by multiple modules, since removing a user requires many cleanup operations:



node_cleanup is run by a mongooseim_cleaner process which subscribes to nodedown messages. Currently, the hook is run inside a global transaction (via global:trans/4).

The job of this hook is to remove all processes registered in Mnesia. MongooseIM uses Mnesia to store processes through which messages are then routed - like user sessions or server-to-server communication channels - or various handlers, e.g. IQ request handlers. Those must obviously be removed when a node goes down, and to do this the modules ejabberd_local, ejabberd_router, ejabberd_s2s, ejabberd_sm and mod_bosh register their handlers with this hook.

Number of retries for this transaction is set to 1 which means that in some situations the hook may be run on more than one node in the cluster, especially when there is little garbage to clean after the dead node. Setting retries to 0 is not good decision as it was observed that in some setups it may abort the transaction on all nodes.


allow == mongoose_hooks:session_opening_allowed_for_user(HostType, JID)

This hook is run after authenticating when user sends the IQ opening a session. Handler function are expected to return:

  • allow if a given JID is allowed to open a new sessions (the default)
  • deny if the JID is not allowed but other handlers should be run
  • {stop, deny} if the JID is not allowed but other handlers should not be run

In the default implementation the hook is not used, built-in user control methods are supported elsewhere. This is the perfect place to plug in custom security control.

Other hooks

  • acc_room_affiliations
  • adhoc_local_commands
  • adhoc_sm_commands
  • amp_check_condition
  • amp_determine_strategy
  • amp_verify_support
  • anonymous_purge
  • auth_failed
  • c2s_stream_features
  • can_access_identity
  • can_access_room
  • caps_recognised
  • check_bl_c2s
  • disco_info
  • disco_local_features
  • disco_local_identity
  • disco_local_items
  • disco_muc_features
  • disco_sm_features
  • disco_sm_identity
  • disco_sm_items
  • does_user_exist
  • extend_inbox_result
  • failed_to_store_message
  • filter_local_packet
  • filter_packet
  • filter_pep_recipient
  • filter_room_packet
  • filter_unacknowledged_messages
  • forbidden_session
  • foreign_event
  • forget_room
  • get_key
  • get_mam_muc_gdpr_data
  • get_mam_pm_gdpr_data
  • get_pep_recipients
  • get_personal_data
  • inbox_unread_count
  • invitation_sent
  • is_muc_room_owner
  • join_room
  • leave_room
  • mam_archive_id
  • mam_archive_message
  • mam_archive_size
  • mam_archive_sync
  • mam_flush_messages
  • mam_get_behaviour
  • mam_get_prefs
  • mam_lookup_messages
  • mam_muc_archive_id
  • mam_muc_archive_message
  • mam_muc_archive_size
  • mam_muc_archive_sync
  • mam_muc_flush_messages
  • mam_muc_get_behaviour
  • mam_muc_get_prefs
  • mam_muc_lookup_messages
  • mam_muc_remove_archive
  • mam_muc_retraction
  • mam_muc_set_prefs
  • mam_remove_archive
  • mam_retraction
  • mam_set_prefs
  • mod_global_distrib_known_recipient
  • mod_global_distrib_unknown_recipient
  • node_cleanup
  • offline_groupchat_message
  • offline_message
  • packet_to_component
  • presence_probe
  • privacy_check_packet
  • privacy_get_user_list
  • privacy_iq_get
  • privacy_iq_set
  • privacy_list_push
  • privacy_updated_list
  • push_notifications
  • register_subhost
  • register_user
  • remove_domain
  • remove_user
  • reroute_unacked_messages
  • resend_offline_messages
  • room_exists
  • room_new_affiliations
  • room_packet
  • roster_get
  • roster_get_jid_info
  • roster_get_subscription_lists
  • roster_get_versioning_feature
  • roster_groups
  • roster_in_subscription
  • roster_out_subscription
  • roster_process_item
  • roster_push
  • roster_set
  • s2s_allow_host
  • s2s_receive_packet
  • s2s_send_packet
  • s2s_stream_features
  • session_cleanup
  • session_opening_allowed_for_user
  • set_presence
  • set_vcard
  • sm_filter_offline_message
  • sm_register_connection
  • sm_remove_connection
  • unacknowledged_message
  • unregister_subhost
  • unset_presence
  • update_inbox_for_muc
  • user_available
  • user_open_session
  • user_ping_response
  • user_receive_iq
  • user_receive_message
  • user_receive_packet
  • user_receive_presence
  • user_receive_xmlel
  • user_send_iq
  • user_send_message
  • user_send_packet
  • user_send_presence
  • user_send_xmlel
  • user_socket_closed
  • user_socket_error
  • user_stop_request
  • user_terminate
  • vcard_set
  • xmpp_bounce_message
  • xmpp_presend_element
  • xmpp_send_element
  • xmpp_stanza_dropped