Smack XEP-0084 - publishing avatar data to node with exception failing XEP-60 5.3 requirements

aTalk just implemented XEP-0084: User Avatar; and is working with PEPListener being correctly triggered on avatar metaData event and a successful retrieving of its avatar Data. However when aTalk tries to publishAvatarData(), it fails with an exception as indicated in the attached log on executing the statement mPepManager.publish(item, node); i.e. failing XEP-60 5.3 requirements.

As per XEP-0060 section 5.3. it seems smack did not set the ‘to’ attribute with the right entity, which may have led to incorrect reply from the server.

5.3 Discover Node Information

Example 13. Entity queries collection node for information

A pubsub service MUST allow entities to query individual nodes for the information associated with that node. The Service Discovery protocol MUST be used to discover this information. The “disco#info” result MUST include an identity with a category of “pubsub” and a type of either “leaf” or “collection”.

Note: If a node has an identity type of “leaf”, then it MUST NOT contain other nodes or collections (only items); if a node has an identity type of “collection”, then it MUST NOT contain items (only other nodes or collections).

Example 13. Entity queries collection node for information

<iq type='get'
    from='francisco@denmark.lit/barracks'
    to='**pubsub.shakespeare.lit**'
    id='info2'>
  <query xmlns='http://jabber.org/protocol/disco#info'
         node='blogs'/>
</iq>

Example 14. Service responds with identity of pubsub/collection

<iq type='result'
    from='pubsub.shakespeare.lit'
    to='francisco@denmark.lit/barracks'
    id='meta1'>
  <query xmlns='http://jabber.org/protocol/disco#info'
         node='blogs'>
    <identity category='pubsub' type='collection'/>
  </query>
</iq>

//============ atalk publishAvatarData() method ================

/**

  • Publish an avatar data.

  • @param id

  • the id of the avatar data

  • @param data

  • the data of the avatar
    */
    private void publishAvatarData(String id, byte[] data)
    {
    AvatarData avatar = new AvatarData(data);
    PayloadItem item = new PayloadItem<>(id, avatar);
    String node = AvatarData.NAMESPACE;

    try {
    mPepManager.publish(item, node);
    }
    catch (SmackException.NotConnectedException | SmackException.NoResponseException
    | InterruptedException | XMPPException.XMPPErrorException e) {
    LOGGER.log(Level.WARNING, "Error while publishing avatar data: " + e.getMessage());
    }
    }

============ aTalk log when attempts to publish userAvatar ===================

07-14 16:57:37.922 D/SMACK: SENT (1): <iq to=‘test@atalk.org’ id=‘qg40p-242’ type=‘get’>

07-14 16:57:37.932 D/SMACK: RECV (1):

07-14 16:57:37.982 E/αTalk: [1] util.UtilActivator.uncaughtException().95 An uncaught exception occurred in thread=Thread[main,5,main] and message was: PubSub service ‘test@atalk.org’ returned disco info result for node ‘urn:xmpp:avatar:data’, but it did not contain an Identity of type ‘leaf’ or ‘collection’ (and category ‘pubsub’), which is not allowed according to XEP-60 5.3.

                        org.jivesoftware.smackx.pubsub.PubSubAssertionError$DiscoInfoNodeAssertionError : PubSub service 'test@atalk.org' returned disco info result for node 'urn:xmpp:avatar:data', but it did not contain an Identity of type 'leaf' or 'collection' (and category 'pubsub'), which is not allowed according to XEP-60 5.3.

at org.jivesoftware.smackx.pubsub.PubSubManager.getNode(PubSubManager.java:255)

at org.jivesoftware.smackx.pep.PEPManager.publish(PEPManager.java:145)

at org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishAvatarData(U serAvatarManager.java:187)

at org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishAvatarData(U serAvatarManager.java:167)

at org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishBitmap(UserA vatarManager.java:279)

at org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishAvatar(UserA vatarManager.java:251)

at org.atalk.android.gui.account.AccountInfoPresenceActivity.onActivityResult(Acco untInfoPresenceActivity.java:1214)

at android.app.Activity.dispatchActivityResult(Activity.java:6548)

at android.app.ActivityThread.deliverResults(ActivityThread.java:4050)

at android.app.ActivityThread.handleSendResult(ActivityThread.java:4097)

at android.app.ActivityThread.access$1400(ActivityThread.java:177)

at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1497)

at android.os.Handler.dispatchMessage(Handler.java:102)

at android.os.Looper.loop(Looper.java:145)

at android.app.ActivityThread.main(ActivityThread.java:5938)

at java.lang.reflect.Method.invoke(Native Method)

at java.lang.reflect.Method.invoke(Method.java:372)

at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1389 )

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1184)

07-14 16:57:37.982 E/αTalk: [1] org.atalk.android.plugin.errorhandler.ExceptionHandler.uncaughtException().78 uncaughtException occurred, killing the process…

                        org.jivesoftware.smackx.pubsub.PubSubAssertionError$DiscoInfoNodeAssertionError : PubSub service 'test@atalk.org' returned disco info result for node 'urn:xmpp:avatar:data', but it did not contain an Identity of type 'leaf' or 'collection' (and category 'pubsub'), which is not allowed according to XEP-60 5.3.

at org.jivesoftware.smackx.pubsub.PubSubManager.getNode(PubSubManager.java:255)

at org.jivesoftware.smackx.pep.PEPManager.publish(PEPManager.java:145)

at org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishAvatarData(U serAvatarManager.java:187)

at org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishAvatarData(U serAvatarManager.java:167)

at org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishBitmap(UserA vatarManager.java:279)

at org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishAvatar(UserA vatarManager.java:251)

at org.atalk.android.gui.account.AccountInfoPresenceActivity.onActivityResult(Acco untInfoPresenceActivity.java:1214)

at android.app.Activity.dispatchActivityResult(Activity.java:6548)

at android.app.ActivityThread.deliverResults(ActivityThread.java:4050)

at android.app.ActivityThread.handleSendResult(ActivityThread.java:4097)

at android.app.ActivityThread.access$1400(ActivityThread.java:177)

at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1497)

at android.os.Handler.dispatchMessage(Handler.java:102)

at android.os.Looper.loop(Looper.java:145)

at android.app.ActivityThread.main(ActivityThread.java:5938)

at java.lang.reflect.Method.invoke(Native Method)

at java.lang.reflect.Method.invoke(Method.java:372)

at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1389 )

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1184)

As per XEP-0060 section 5.3. it seems smack did not set the ‘to’ attribute with the right entity, which may have led to incorrect reply from the server.
No, PEP nodes are placed under the user’s account.

From the log, the problem seems to occur during the "Discover Node Information process - disco#info), and not in process. Shouldn’t this be addressed to =‘pubsub.atalk.org’ instead of the user account?

07-14 16:57:37.922 D/SMACK: SENT (1):

I cut and paste the same and send it from within the pidgin xmpp console, I got the reply as required by XEP-0060 section 5.3. Look like my earlier assumption is not correct. Can it be that aTalk misses out some entityCapabilities that are required to be included for EntityCapsManager to advertised?

========= Pidgin xmpp console iq response ============

I found that smackx.omemo.OmemService also throws the similar exception. And in the source it is reported as

ejabberd bug: https://github.com/processone/ejabberd/issues/1717 when an invalid-node info is requested.

11:26:52.465 WARNING: [3] org.jivesoftware.smackx.omemo.OmemoService.refreshOwnDeviceList() Caught a PubSubAssertionError when fetching a deviceList node. This probably means that we’re dealing with an ejabberd server and the LeafNode does not exist.

Omemo catches the Exception and allows the application to proceed without crashing the xmpp client. But not the case when the invalid node is

node=‘urn:xmpp:avatar:data’; It causes the whole application to crash. Should it also adapt the same as omemo and proceed without the information, since this info is not absolutely necessary and knowing that there is none.

I also wonder how ejabberd is going to retrieve the

to send, when there is not such node data to extract the info from, or did I misunderstood in somewhere?

I have sent the observation to ejabberd development team for their investigation. Meantime decided to implement a temporary solution for aTalk to publish XEP-0084 User Avatar using reflection as below; on catching PubSubAssertionError.DiscoInfoNodeAssertionError:

Would appreciate if the smack team will again take a look at my earlier proposal.

/**

  • Alternate aTalk nodePublish if PepManager#publish() failed
  • @param item
  • the item to publish.
  • @param node
  • the node to publish on.
  • @return true if publish is successful
    */
    private boolean nodePublish(Item item, String node)
    {
    PubSubManager pubSubManager = PubSubManager.getInstance(mConnection, mAccount);
    try {
    LeafNode pubSubNode = getLeafNode(pubSubManager, node);
    if (pubSubNode != null) {
    pubSubNode.publish(item);
    return true;
    }
    }
    catch (SmackException.NotConnectedException | InterruptedException e) {
    e.printStackTrace();
    }
    return false;
    }

/**

  • Get an instance of leafNode to publish avatar data
  • @param pubSubManager
  • the PubSubManager
  • @param nodeId
  • the node to publish on.
  • @return an instance of leafNode
    */
    private LeafNode getLeafNode(PubSubManager pubSubManager, String nodeId)
    {
    try {
    Constructor getLeafNode = LeafNode.class.getDeclaredConstructor(PubSubManager.class, String.class);
    getLeafNode.setAccessible(true);
    return (LeafNode) getLeafNode.newInstance(pubSubManager, nodeId);
    }
    catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
    e.printStackTrace();
    }
    return null;
    }

Following are my observations after the above implementation:

  1. On first attempt to pubsub avatar data/metadata, the PubSubManager#getNode() will always throw PubSubAssertionError.DiscoInfoNodeAssertionError() i.e. failed XEP-60 5.3.

  2. The above reflection implementation then takes over to continue pubsub avatar data/metadata to the ejabberd server.

  3. After the process #2, ejabberd server now has a pre-stored avatar data/metadata in its database

  4. Second attempt to re-pubsub avatar data/metadata will response correctly to the disco#info i,e, pass XEP-60 5.3.

5 There are cases when disco#info for avatar.metadata will failed with reply timeout. Look like server needs time to store large amount of image data?

  1. Found that Item #5 is likely to pass on subsequent pubsub attempts.

It seems ejabberd server needs to have a pre-stored avatar data/metadata to properly response to disco#info from client i.e. according to XEP-60 5.3. requirements. Is this the chicken and egg issue???

========= aTalk log for avatar data/metadata pubsub to ejabberd server ===============

07-31 17:16:00.029 D/SMACK: SENT (0):

07-31 17:16:00.039 D/SMACK: RECV (0):

07-31 17:16:00.039 W/αTalk: [1] org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishAvatarData() Use aTalk own nodePublish: PubSub service ‘leopard@atalk.org’ returned disco info result for node ‘urn:xmpp:avatar:data’, but it did not contain an Identity of type ‘leaf’ or ‘collection’ (and category ‘pubsub’), which is not allowed according to XEP-60 5.3.

=== avatar data published by nodePubsub ====

07-31 17:16:00.069 D/SMACK: SENT (0): iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABH NCSVQICAgIfAhkiAAAIABJREFU

eJzNnXm0ZVV95z977zPc+Y01V1FzFRSDCC2jDCJxQIyJCWA0dtTERGMShzaro91pYKEmmrHTGkxM

VkLK1ZBGvUyjXqJRL1GphlRrZarVEvV6mUajTK0eUi0HVCsBj//kEMuW1BBC4ilJ2Zf4nqQcOsei

Vvap1wIqlZBqxadS8vB9j69/+SunAsXcpe/OygOv2Fq96ugh4ahZ+HoBVxbOVE2CVm4TqB9JFqpc

UpRL

07-31 17:16:00.069 D/SMACK: SENT (0):

07-31 17:16:04.619 D/SMACK: RECV (0):

07-31 17:16:04.639 D/SMACK: RECV (0): <iq xml:lang=‘en’ to=‘leopard@atalk.org/atalk’ from=‘leopard@atalk.org’ type=‘result’ id='3a4dy-132’>

07-31 17:16:04.649 W/αTalk: [1] org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishAvatarMetaDa ta() Use aTalk own nodePublish: PubSub service ‘leopard@atalk.org’ returned disco info result for node ‘urn:xmpp:avatar:metadata’, but it did not contain an Identity of type ‘leaf’ or ‘collection’ (and category ‘pubsub’), which is not allowed according to XEP-60 5.3.

=== avatar metadata published by nodePubsub ====

07-31 17:16:04.669 D/SMACK: SENT (0):

========= Second attempt to pubsub avatar data/metadata (after server has pre-stored avatar data/metadata) =========

07-31 17:16:07.259 D/SMACK: SENT (0):

07-31 17:16:07.599 D/SMACK: RECV (0): <feature

var=‘http://jabber.org/protocol/pubsub#retrieve-items’/>

07-31 17:16:07.609 D/SMACK: SENT (0):

07-31 17:16:07.709 D/SMACK: RECV (0): 01creation@001501:492492:649046creation@001501:492492:649 046iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABH NCSVQICAgIfAhkiAAAIABJREFU

eJzNnXm0ZVV95z977zPc+Y01V1FzFRSDCC2jDCJxQIyJCWA0dtTERGMShzaro91pYKEmmrHTGkxM

otHEtMGAKKKAKJMYZkEoqqDm8Q31hjuece/df+xz6xWsJNarKujstc4699537zn7fPdv3r/f7wle

oMXFue+XgDOzwWXR66K8ItEuLN8tgqM7DFxlQJyQIOl8xssKIPBv/JOC/rGtKHTxijmpI+amLewF

llhYVVBy/z84vDz/dOA/5bDWCndcJ6210tp/Vtba4viBN3f0P/tn5b53nez/9v/3M/w/OCLU2R3n

Av0AAAAASUVORK5CYII=

07-31 17:16:07.719 I/αTalk: [10] org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.downloadAvatar() Avatar with new avatarHash received (old => new) from leopard@atalk.org

c79cb1a24621fecf5c3826e003f939af8f2c2608

abe561ec6afa4839137db5f70899c8a148186c0c

07-31 17:16:14.549 D/SMACK: SENT (0): iVBORw0KGgoAAAANSUhEUgAAAHgAAABuCAIAAABjiUADAAAAA3 NCSVQICAjb4U/gAAAgAElEQVR4

nEy7Z5Rl53Ud+OWbXs6vXr3KuatzTkAjB2YOBQ5FjwIteVkaa1mytTTSaJap5TWWvSTKomiJFCWa

JkgxgiACkQkQYANooLvR6Byquyvnejne8KX5UbTG99f9vc+5+5yz977wr//iPzNqGoYT+CIIuBAC…

GaKMmBBppaXvepSS+VvX4vHI8PBwOBSl0Gh3AzuSqDS7p995N987kO0pKo

07-31 17:16:14.549 D/SMACK: SENT (0):

07-31 **17:16:24.**499 W/αTalk: [1] org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishAvatarMetaDa ta() Use aTalk own nodePublish: No response received within reply timeout. Timeout was 10000ms (~10s). Waited for response using: IQReplyFilter: iqAndIdFilter (AndFilter: (OrFilter: (IQTypeFilter: type=error, IQTypeFilter: type=result), StanzaIdFilter: id=3a4dy-143)), : fromFilter (OrFilter: (FromMatchesFilter (full): leopard@atalk.org, FromMatchesFilter (full): null)).

07-31 17:16:24.509 D/SMACK: SENT (0):

07-31 17:16:35.649 D/SMACK: RECV (0):

07-31 17:16:35.669 D/SMACK: RECV (0): <feature …

var=‘http://jabber.org/protocol/pubsub#retrieve-items’/>

07-31 17:16:35.689 D/SMACK: RECV (0):

07-31 17:16:35.689 D/SMACK: RECV (0):

====== disco#info for avatar:metadata reply pass on third attempt ============

07-31 17:29:47.069 D/SMACK: SENT (0):

07-31 17:29:47.139 D/SMACK: RECV (0):

07-31 17:29:47.139 D/SMACK: RECV (0):

ejabberd has just pushed a fix for 17.08 release for the reported problem. However aTalk will still keep the patch as there are still many existing old ejabberd servers in operation.

ejabberd does not return the PubSub/PEP node identity in disco#info results, although it is required by XEP-0060 § 5.3 ·…

For now please kindly close the issue.

1 Like

After testing the fix from ejabberd, it is not working as expected. Please refer to

ejabberd does not return the PubSub/PEP node identity in disco#info results, although it is required by XEP-0060 § 5.3 ·…

on the latest development on ejabberd specific to this requirements.

Appreciate if some one from smack team can comment.

Not sure if my information about the current situation is up to date. But not returning item-not-found when the PubSub node does not exists appears to be just wrong. And so is returning an IQ result on a PubSub node query which does not contain an .

Happy to be proven otherwise by the protocol lawyers.

If you refer to my earlier comment

cmeng Jul 31, 2017 2:59 AM (in response to cmeng)

smack is expecting proper reply from ejabberd even when node does not exist.

I have to do some patch in aTalk code to made avatar publish work right now.

cmeng Jul 30, 2017 11:31 PM (in response to cmeng)

I also had to deploy a workaround in smack-omemo for that. Its necessary to provide backwards compatibility to older ejabberd servers even when the issue is fixed in newer versions.

Define “proper reply”.

Below is the attached log - bold in the reply is one of the attributes that smack is expecting to have in getNode().infoReply in first log attached. Otherwise it throws DiscoInfoNodeAssertionError as shown in second log attached:

pubSubManager.getNode(node) ===>

DiscoverInfo infoReply = connection().createStanzaCollectorAndSend(info).nextResultOrThrow();
if (infoReply.hasIdentity(PubSub.ELEMENT, “leaf”)) {
node = new LeafNode(this, id);
}
else if (infoReply.hasIdentity(PubSub.ELEMENT, “collection”)) {
node = new CollectionNode(this, id);
}
else {
// XEP-60 5.3 states that
// “The ‘disco#info’ result MUST include an identity with a category of ‘pubsub’ and a type of either ‘leaf’ or ‘collection’.”
// If this is not the case, then we are dealing with an PubSub implementation that doesn’t follow the specification.
throw new PubSubAssertionError.DiscoInfoNodeAssertionError(pubSubService, id);
}

========= aTalk log for avatar data/metadata disco#info when the entity node is presence ===============

07-31 17:16:07.259 D/SMACK: SENT (0):

07-31 17:16:07.599 D/SMACK: RECV (0): <feature

var=‘http://jabber.org/protocol/pubsub#retrieve-items’/>

07-31 17:16:07.609 D/SMACK: SENT (0):

========= aTalk log for avatar data/metadata disco#info when the entity node is not presence ===============

07-31 17:16:00.029 D/SMACK: SENT (0):

07-31 17:16:00.039 D/SMACK: RECV (0):

07-31 17:16:00.039 W/αTalk: [1] org.jivesoftware.smackx.avatar.useravatar.UserAvatarManager.publishAvatarData() Use aTalk own nodePublish: PubSub service ‘leopard@atalk.org’ returned disco info result for node ‘urn:xmpp:avatar:data’, but it did not contain an Identity of type ‘leaf’ or ‘collection’ (and category ‘pubsub’), which is not allowed according to XEP-60 5.3.

Holger provided me with some helpful clarifications (Thanks Holger!). I’ve re-opened [SMACK-759] PubSubManager.getLeafNode() throws PubSubAssertionError.DiscoInfoNodeAssertionError if node exists but its n… and adjusted the issue title and description.