processone / ejabberd

Robust, Ubiquitous and Massively Scalable Messaging Platform (XMPP, MQTT, SIP Server)
https://www.process-one.net/en/ejabberd/
Other
6.03k stars 1.5k forks source link

Groupchat messages are not receiving to user which uses "mod_client_state" presences #4012

Closed wtomaszewski-applause closed 1 year ago

wtomaszewski-applause commented 1 year ago

Environment

Configuration (only if needed):

  mod_muc:
    hosts: ["myhost"]
    db_type: sql
    ram_db_type: sql
    access: muc
    access_create: muc_admin
    access_persistent: muc_admin
    access_admin: muc_admin
    max_users: 2500
    max_user_conferences: 2500
    ## ejabber keeps N last messages in memory, but there is no way to flush
    ## this cache we have to keep this cache at zero level for deleting messages
    history_size: 0
    default_room_options:
      allow_change_subj: true
      allow_private_messages: false
      allow_visitor_nickchange: false
      anonymous: false
      captcha_protected: false
      logging: true
      mam: true
      max_users: 500
      members_by_default: true
      persistent: true
      public: false
      public_list: false
  mod_offline:
    access_max_user_messages: max_user_offline_messages
  mod_mam:
    default: always
    db_type: sql
    assume_mam_usage: true
    use_cache: false
  mod_client_state:
    queue_chat_states: true
    queue_presence: true
```yaml
loglevel: 4
...

Errors from error.log/crash.log

No errors

Bug description

I would like to use mod_client_state module from ejabberd to disable all websocket message handlers when user is in background mode and enable when user is going back to available mode. I'm using https://github.com/strophe/strophejs library for which I'm using MUC plugin to send join presence to specific chat room. I have client implementation to receive groupchat and chat (private) messages:

this._connection.addHandler((stanza) => this._handleDirectMessage(stanza), null, 'message', 'chat')
this._connection.addHandler((stanza) => this._handleRoomMessage(stanza), null, 'message', 'groupchat')

That code is working and I'm able to receive all new messages in real time by above handlers. I used mod_client_state and I go to unavailable mode than messages are not received for both handlers which is expected and desired here. But when I go to available mode, then the first handler for private messages receives queued messages immediately but the second handler for groupchat stops working and I need to execute join function from MUC plugin again but it seems that handler is not longer receiving any groupchat messages.

    this._connection.send($pres({
      xmlns: Strophe.NS.CLIENT,
      type: 'available',
    }).c('csi', { xmlns: Strophe.NS.CSI }).up());
    this._connection.send($pres({
      xmlns: Strophe.NS.CLIENT,
      type: 'unavailable',
    }).c('csi', { xmlns: Strophe.NS.CSI, 'ext': 'urn:xmpp:csi:0' }).up());
$pres({
        from: this._connection.jid,
        to: room_nick
      }).c("x", {
        xmlns: Strophe.NS.MUC
      })

It seems that only groupchat messages are not working with mod_client_state plugin because for chat or custom messages I'm able to receive queued messages in handlers. I did a look and for groupchat messages when user in unavailable, the messages are not stored in spool table but private messages are stored in that table.

Below I'm sending message example. If the user uses mod_client_state then for groupchat messages, that user will not be visible in destinations list until user will reconnect websocket connection which is something which I want to avoid.

11:36:35.744 [debug] Route multicast:
#message{
    id = <<"3fa4bed5-7296-4956-a767-938b557ff533">>,type = groupchat,
    lang = <<"en">>,
    from =
        #jid{
            user = <<"testcycle_3_announce">>,
            server = <<"myhost">>,
            resource = <<"ba824a5b-dd94-45e2-b548-2eacc096cde2">>,
            luser = <<"testcycle_3_announce">>,
            lserver = <<"myhost">>,
            lresource = <<"ba824a5b-dd94-45e2-b548-2eacc096cde2">>},
    to =
        #jid{
            user = <<"testcycle_3_announce">>,
            server = <<"myhost">>,resource = <<>>,
            luser = <<"testcycle_3_announce">>,
            lserver = <<"myhost">>,lresource = <<>>},
    subject = [],
    body = [#text{lang = <<>>,data = <<"uyuy">>}],
    thread = #message_thread{parent = <<>>,data = <<"null">>},
    sub_els =
        [#mam_archived{
             by =
                 #jid{
                     user = <<"testcycle_3_announce">>,
                     server = <<"myhost">>,resource = <<>>,
                     luser = <<"testcycle_3_announce">>,
                     lserver = <<"myhost">>,
                     lresource = <<>>},
             id = <<"1679567795727521">>},
         #stanza_id{
             by =
                 #jid{
                     user = <<"testcycle_3_announce">>,
                     server = <<"myhost">>,resource = <<>>,
                     luser = <<"testcycle_3_announce">>,
                     lserver = <<"myhost">>,
                     lresource = <<>>},
             id = <<"1679567795727521">>},
         #xmlel{
             name = <<"x">>,
             attrs = [{<<"xmlns">>,<<"jabber:x:event">>}],
             children =
                 [#xmlel{name = <<"composing">>,attrs = [],children = []}]},
         #xmlel{
             name = <<"temporary-id">>,attrs = [],
             children =
                 [{xmlcdata,<<"928b5025-3258-4659-94e6-00c7016e55e6">>}]}],
    meta =
        #{ip => {127,0,0,1},
          mam_archived => true,
          muc_sender_real_jid =>
              #jid{
                  user = <<"ba824a5b-dd94-45e2-b548-2eacc096cde2">>,
                  server = <<"myhost">>,
                  resource = <<"85979c89-eb4d-4481-9a84-07943bf6a1c5">>,
                  luser = <<"ba824a5b-dd94-45e2-b548-2eacc096cde2">>,
                  lserver = <<"myhost">>,
                  lresource = <<"85979c89-eb4d-4481-9a84-07943bf6a1c5">>},
          stanza_id => 1679567795727521}}
Domain: myhost
Destinations: e3fd892a-13ed-44e3-94c4-f4cb2a374de6@myhost/69076861-58c1-45d5-8ed9-1fc4ad9d3f82, e3fd892a-13ed-44e3-94c4-f4cb2a374de6@myhost/4585db7a-1786-4b0a-9931-2555d2136a5e, ba824a5b-dd94-45e2-b548-2eacc096cde2@myhost/85979c89-eb4d-4481-9a84-07943bf6a1c5

Overall I would like to report two bugs here:

  1. When user uses unavailable and available presence with CSI xmlns, then user jid is not longer visible in Destinations list.
  2. Messages with groupchat type are not stored in spool table.
prefiks commented 1 year ago

When you send presence unavailable, you also trigger leaving muc room (muc uses presence to track which users are in room, when it receives presence unavailable user is removed from a list). This will stop muc from sending message to you - you are no longer room member (this explain why there no muc messages in offline storage). Sending presence after reconnect will also not join room for you, it will send presence to entries in your roster, and i guess your room is not in that list, this is why you need to explicitly trigger room join.

There is https://docs.ejabberd.im/developer/xmpp-clients-bots/extensions/muc-sub/ that allow you to make muc send you notifications about messages without using presence based tracking.

wtomaszewski-applause commented 1 year ago

I appreciate you providing the background on why that problem is occurring. Just to be sure, if I want to use the muc-sub extension, do I need to use it for all of my private, groupchat, and customized notifications as well? Mod client state must be replaced with the muc sub extension? In my solution, I have a logic that runs "unavailable" when a user enters background mode in a mobile application, so that it applies to various notification types in addition to "groupchat" messages.

wtomaszewski-applause commented 1 year ago

With using the mucsub extension and the suggested method, I was able to resolve that issue. For groupchat message types, I used mucsub to send a subscribe iq stanza to the chat room instead of joining the chat room using MUC join which sends an "available" presence. Then, as I switch to the unavailable mode, I send an mucsub iq stanza to unsubscribe. After switching back to the available state and subscribing again, I started getting the queued messages instantly. Thank you very much for your quick help in solving the problem, seems the mod_client_state requires mucsub iq subscriptions instead of presence stanzas for groupchat messages.

One observation, when I enabled allow_subscription: true for mod_muc in ejabberd config, the modmuc subscription for existing chat rooms returns Subscriptions are not allowed error. But when I created new chatroom that subscriptions were working. Not sure if that is configuration issue, maybe I'm missing some config which allows subscription for existing chatrooms.