According to XEP-0045, getting a list of owners for a MUC and granting ownership for a MUC are done with an iq stanza that contains a query object in the muc#admin namespace. Please see examples 181 and 183 in the section of XEP-0045 regarding modifying the owner list.
Currently, Smack implements those two actions with a muc#owner namespace. Smack uses private methods MultiUserChat.changeAffiliationByOwner and MultiUserChat.getAffiliatesByOwner for the two actions. Both methods create iqs of the type MUCOwner (which prints out a muc#owner namespace). These methods need to be changed to iqs of type MUCAdmin to properly follow the spec. That appears to be an easy fix since other actions that these methods are used for (granting admin rights, get a list of admins) also need the muc#admin namespace. I haven’t done thorough analysis of the MultiUserChat to see all that those methods do; just enough to debug my ownership related problem. So that statement of it being an easy fix could be wrong.
After doing some digging, it seems the root of the problem is the fact that these actions were actually in the muc#owner namespace back in an older version of the XEP-0045 spec. To be specific, these actions changed to the muc#admin namespace on version 1.19 of the xep-0045 spec, which was apparently published in April 2005 (and refered to as jep-0045). Afterward, it appears most server code still allowed the muc#owner namespace for legacy reasons, which is why, for most servers, Smack still can perform the actions with the muc#owner namespace without any problems. But for newer server code - namely Prosody, which is what I was using when this bug presented and was created in Aug. 2008 - Smack doesn’t work properly.
You can test this by creating a MUC on a prosody server (i.e. neko.im, thiessen.im, or your own local server), then trying to view the owner list. It shouldn’t work and didn’t for me. This is because prosody doesn’t recognize the muc#owner. And, according to the spec, it shouldn’t.
Again, the fix is to use the MUCAdmin type to get the muc#admin namespace. I’m currently doing this with my own MUCHack class, which is inculded below. For the most part, I simply copy-pasted the two methods I mentioned earlier and changed the MUCOwner to MUCAdmin. It works fine on both prosody and non-prosody servers. (Again, I haven’t done thorough testing.)
A couple notes on MUCHack:
-
I implemented it via static methods for my own purposes.
-
the constructor for the Affiliate class only has package visibility; so MUCHack has to be in the org.jivesoftware.smackx.muc package.
public class MUCHack {
@SuppressWarnings("rawtypes")
public static Collection<Affiliate> getAffiliates(MultiUserChat muc,XMPPConnection con,String affiliation) throws XMPPException{
MUCAdmin iq = new MUCAdmin();
iq.setTo(muc.getRoom());
iq.setType(IQ.Type.GET);
// Set the specified affiliation. This may request the list of owners/admins/members/outcasts.
MUCAdmin.Item item = new MUCAdmin.Item(affiliation,null);
iq.addItem(item); // Wait for a response packet back from the server.
PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID());
PacketCollector response = con.createPacketCollector(responseFilter);
// Send the request to the server.
con.sendPacket(iq);
// Wait up to a certain number of seconds for a reply.
MUCAdmin answer = (MUCAdmin) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
// Stop queuing results
response.cancel(); if (answer == null) {
throw new XMPPException("No response from server.");
}
else if (answer.getError() != null) {
throw new XMPPException(answer.getError());
}
// Get the list of affiliates from the server's answer
List<Affiliate> affiliates = new ArrayList<Affiliate>();
for (Iterator it = answer.getItems(); it.hasNext();) {
affiliates.add(new Affiliate((MUCAdmin.Item) it.next()));
}
return affiliates;
} public static void grantAffiliation(MultiUserChat muc,XMPPConnection con,String affiliation,String jid) throws XMPPException{
MUCAdmin iq = new MUCAdmin();
iq.setTo(muc.getRoom());
iq.setType(IQ.Type.SET);
// Set the new affiliation.
MUCAdmin.Item item = new MUCAdmin.Item(affiliation,null);
item.setJid(jid);
iq.addItem(item); // Wait for a response packet back from the server.
PacketFilter responseFilter = new PacketIDFilter(iq.getPacketID());
PacketCollector response = con.createPacketCollector(responseFilter);
// Send the change request to the server.
con.sendPacket(iq);
// Wait up to a certain number of seconds for a reply.
IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
// Stop queuing results
response.cancel(); if (answer == null) {
throw new XMPPException("No response from server.");
}
else if (answer.getError() != null) {
throw new XMPPException(answer.getError());
}
}
}
On a sidenote, this part of the spec changed 7 years ago. Is there not a whole lot of work being done on the Smack side to stay with updates in the spec? Is this common with all libraries? common with XMPP as a whole? I’m not sure how often the spec updates or how big the changes are when there are updates. (If there aren’t many changes, code being on an older spec doesn’t really pose an issue I guess.) Just trying to figure out how big of a deal this is. 7 years makes me nervous.
MUCHack.java.zip (948 Bytes)