When a messaging client starts, it typically builds a UI showing a list of recent chats, with metadata attached to them like, whether any chat has new messages and how many, or if it is fully read, or if they are for example muted and until when.
In MongooseIM this functionality is provided by mod_inbox.
It is personal to a given user and represents the current status of the conversations of that user. It's the front-page of the chat feature.
It is a specific conversation, that the user can identify by the recipient jid, that is, the user jid in case of a one-to-one chat, or the room jid in case of a group-chat.
Box (also referred to as "folder")
A category where entries can be classified. The default box is the active box, simply called inbox. There is a second box, called archive, where entries can be thrown into and not displayed by default. More boxes can be created through configuration.
Entity Use Cases
Discovering Inbox Services
An entity can discover the inbox service via a Features Discovery request:
1 2 3 4 5 6 7 8 9101112
<!-- Client --><iqtype='get'id='a96d4244760853af7b3ae84faa1a40fb'to='localhost'><queryxmlns='http://jabber.org/protocol/disco#info'/></iq><!-- Server --><iqfrom='localhost'to='alice@localhost/res1'id='a96d4244760853af7b3ae84faa1a40fb'type='result'><queryxmlns='http://jabber.org/protocol/disco#info'><identitycategory='server'type='im'name='MongooseIM'/><featurevar='erlang-solutions.com:xmpp:inbox:0'/></query></iq>
Fetching the inbox
The inbox is fetched using regular XMPP Data Forms. To request the supported form, the client should send:
<!-- Client --><iqtype='get'id='some_unique_id'><queryxmlns='erlang-solutions.com:xmpp:inbox:0'/></iq><!-- Server --><iqfrom='alice@localhost'to='alice@localhost/res1'id='some_unique_id'type='result'><queryxmlns='erlang-solutions.com:xmpp:inbox:0'><xxmlns='jabber:x:data'type='form'><fieldtype='hidden'var='FORM_TYPE'><value>erlang-solutions.com:xmpp:inbox:0</value></field><fieldvar='start'type='text-single'/><fieldvar='end'type='text-single'/><fieldvar='order'type='list-single'><value>desc</value><optionlabel='Ascending by timestamp'><value>asc</value></option><optionlabel='Descending by timestamp'><value>desc</value></option></field><fieldvar='hidden_read'type='text-single'value='false'/><fieldvar='box'type='list-simple'value='all'><optionlabel='all'><value>all</value></option><optionlabel='inbox'><value>inbox</value></option><optionlabel='archive'><value>archive</value></option><optionlabel='bin'><value>bin</value></option></field><fieldvar='archive'type='boolean'/></x></query></iq>
where none-or-many message stanzas are sent to the requesting resource describing each inbox entry, and a final iq-fin stanza marks the end of the inbox query,
Inbox query result IQ stanza returns the following values:
count: the total number of conversations (if hidden_read value was set
to true, this value will be equal to active_conversations)
unread-messages: total number of unread messages from all
active-conversations: the number of conversations with unread
Note that the queryid field is optional, and if not provided, the answers will fall back to the id field of the IQ query.
Filtering and ordering
Inbox query results may be filtered by time range and box, and sorted by timestamp.
By default, mod_inbox returns all conversations, listing the ones updated most recently first.
A client may specify the following parameters:
variable start: Start date for the result set (value: ISO timestamp)
variable end: End date for the result set (value: ISO timestamp)
variable order: Order by timestamp (values: asc, desc)
variable hidden_read: Show only conversations with unread messages (values: true, false)
variable box: Indicate which box is desired. Supported are all, inbox, archive and bin. More boxes can be implemented, see mod_inbox – Boxes. If not provided, all except the bin are returned.
variable archive [deprecated, prefer box]: whether to query the archive inbox. true means querying only the archive box, false means querying only the active box. If the flag is not set, it is assumed all entries are requested. This is kept for backwards compatibility reasons, use the box flag instead.
They are encoded inside a standard XMPP Data Forms format.
Dates must be formatted according to XMPP Date and Time Profiles.
It is not mandatory to add an empty data form if a client prefers to use default values (<inbox/> element may be empty).
However, the IQ type must be "set", even when the data form is missing.
Limiting the query
It can happen that the amount of inbox entries is too big for a given user, even after filtering by start and end as already available in mod_inbox. Hence, we need to set a fixed limit of the number of entries that are requested. For this, we can use a <max> attribute as defined in XEP-0059: #2.1 Limiting the Number of Items:
Given an entry, certain properties are defined for such an entry:
Clients usually have two different boxes for the inbox: the regular one, simply called the inbox (or the active inbox), and an archive box, where clients can manually throw conversations they don't want displayed in the default UI. A third box is the trash bin, where deleted entries go and are cleaned up in regular intervals.
It is expected that entries will reside in the archive until they're either manually moved back to the active box, or they receive a new message: in such case the entry should jump back to the active box automatically.
More boxes can be implemented, see mod_inbox#boxes. Movement between boxes can be achieved through the right XMPP IQ, no more automatic movements are developed as in the case of inbox-archive.
Entries keep a count of unread messages that is incremented automatically upon receiving a new message, and (in the current implementation) set to zero upon receiving either a message by one-self, or an appropriate chat marker as defined in XEP-0333 (which markers reset the count is a matter of configuration, see doc).
This property can also be manually set to zero or to one using the appropriate requests as explained below.
Entries can be muted for given periods of time, and likewise, unmuted. This changes the UI representation, and also, means that the user won't get PNs (Push Notifications) for this entry, until the time set expires, or the user sets otherwise. Knowledge of this is necessary to help build the UI.
Expected times can be extended before the period has expired, without the need to first unmuting. When muting a conversation, the final timestamp will be calculated by the server as the current time plus the requested period, in seconds, to centralise knowledge of UTC clocks. When muting an already muted conversation, the timestamp is simply overridden following the previous specification.
No more properties are expected, but one could envisage notions of flagging conversations with different colours, for example according to their urgency, or a client-specific category (work, personal, fitness, and whatnot), or pins to denote an entry should be always displayed (possibly in a special format, like on top of the box). The design of the protocol, and the implementation, aims to leave room for future extensions.
To fetch all supported properties, a classic Data Form is used. Upon the client sending an iq-get without a jid:
Setting properties is done using the standard XMPP pattern of iq-query and iq-result, as below:
<iqid='some_unique_id'type='set'><queryxmlns='erlang-solutions.com:xmpp:inbox:0#conversation'jid='bob@localhost'><Property>Value</Property><!-- Possibly other properties --></query></iq>
where Property and Value are a list of key-value pairs as follows:
box: inbox, archive, or a custom value if this has been extended.
archive: true or false
mute: number of seconds to mute for. Choose 0 for unmuting.
read (adjective, not verb): true or false. Setting to true essentially sets the unread-count to 0, false sets the unread-count to 1 (if it was equal to 0, otherwise it lefts it unchanged). No other possibilities are offered, to reduce the risk of inconsistencies or problems induced by a faulty client.
Note that resetting the inbox count will not be forwarded. While a chat marker will be forwarded to the interlocutor(s), (including the case of a big groupchat with thousands of participants), this reset stanza will not.
If the query was successful, the server will answer with two stanzas, following the classic pattern of broadcasting state changes. First, it would send a message with a <x> children containing all new configuration, to the bare-jid of the user: this facilitates broadcasting to all online resources to successfully synchronise their interfaces.
Here jid is the bare jid of the user whose inbox we want to reset. This action
does not change the last message stored in inbox; meaning that neither this
stanza nor anything given within will be stored; the only change is the inbox
unread_count is set to zero.
<!-- Alice sends: --><messagetype="chat"to="bob@localhost/res1"id=”123”><body>Hello</body></message><!-- Bob receives: --><messagefrom="alice@localhost/res1"to="bob@localhost/res1"id=“123”xml:lang="en"type="chat"><body>Hello</body></message><!-- Alice sends: --><iqtype="set"id="10bca"><inboxxmlns="erlang-solutions.com:xmpp:inbox:0"queryid="b6"><xxmlns='jabber:x:data'type='form'><fieldtype='hidden'var='FORM_TYPE'><value>erlang-solutions.com:xmpp:inbox:0</value></field><fieldtype='text-single'var='start'><value>2018-07-10T12:00:00Z</value></field><fieldtype='text-single'var='end'><value>2018-07-11T12:00:00Z</value></field><fieldtype='list-single'var='order'><value>asc</value></field></x></inbox></iq><!-- Alice receives: --><messagefrom="alice@localhost"to="alice@localhost"id="9b759"><resultxmlns="erlang-solutions.com:xmpp:inbox:0"unread="0"queryid="b6"><forwardedxmlns="urn:xmpp:forward:0"><delayxmlns="urn:xmpp:delay"stamp="2018-07-10T23:08:25.123456Z"/><messagexml:lang="en"type="chat"to="bob@localhost/res1"from="alice@localhost/res1"id=”123”><body>Hello</body></message></forwarded><box>inbox</box><archive>false</archive><mute>0</mute></result></message><iqfrom="alice@localhost"to="alice@localhost/res1"id="10bca"type="result"><finxmlns='erlang-solutions.com:xmpp:inbox:0'><count>1</count><unread-messages>0</unread-messages><active-conversations>0</active-conversations></fin></iq>
Example error response
1 2 3 4 5 6 7 8 9101112131415161718192021
<!--Alice sends request with invalid value of start field: --><iqtype='set'id='a78478f20103ff8354d7834d0ba2fdb2'><inboxxmlns='erlang-solutions.com:xmpp:inbox:0'><xxmlns='jabber:x:data'type='submit'><fieldtype='text-single'var='start'><value>invalid</value></field></x></inbox></iq><!--Alice receives an error with description of the first encountered invalid value: --><iqfrom='alice@localhost'to='alice@localhost/res1'id='a78478f20103ff8354d7834d0ba2fdb2'type='error'><errorcode='400'type='modify'><bad-rquestxmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/><textxmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>
Invalid inbox form field value, field=start, value=invalid