IQAdminHandler.java 17.4 KB
Newer Older
1 2 3 4 5
/**
 * $RCSfile$
 * $Revision: 1125 $
 * $Date: 2005-03-14 15:59:37 -0300 (Mon, 14 Mar 2005) $
 *
6
 * Copyright (C) 2004-2008 Jive Software. All rights reserved.
7
 *
8 9 10 11 12 13 14 15 16 17 18
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
19 20
 */

21
package org.jivesoftware.openfire.muc.spi;
22

23 24 25
import java.util.ArrayList;
import java.util.List;

26
import org.dom4j.Element;
27
import org.jivesoftware.openfire.PacketRouter;
28 29 30 31 32
import org.jivesoftware.openfire.muc.CannotBeInvitedException;
import org.jivesoftware.openfire.muc.ConflictException;
import org.jivesoftware.openfire.muc.ForbiddenException;
import org.jivesoftware.openfire.muc.MUCRole;
import org.jivesoftware.openfire.muc.NotAllowedException;
33
import org.jivesoftware.openfire.user.UserNotFoundException;
34
import org.jivesoftware.util.JiveGlobals;
35 36 37 38 39 40 41 42 43 44 45 46 47 48
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.Presence;

/**
 * A handler for the IQ packet with namespace http://jabber.org/protocol/muc#admin. This kind of 
 * packets are usually sent by room admins. So this handler provides the necessary functionality
 * to support administrator requirements such as: managing room members/outcasts/etc., kicking 
 * occupants and banning users.
 *
 * @author Gaston Dombiak
 */
public class IQAdminHandler {
49
	
50
	private final LocalMUCRoom room;
51

52
    private final PacketRouter router;
53

54
    private final boolean skipInvite;
55

56
    public IQAdminHandler(LocalMUCRoom chatroom, PacketRouter packetRouter) {
57 58
        this.room = chatroom;
        this.router = packetRouter;
59
        this.skipInvite = JiveGlobals.getBooleanProperty("xmpp.muc.skipInvite", false);
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    }

    /**
     * Handles the IQ packet sent by an owner or admin of the room. Possible actions are:
     * <ul>
     * <li>Return the list of participants</li>
     * <li>Return the list of moderators</li>
     * <li>Return the list of members</li>
     * <li>Return the list of outcasts</li>
     * <li>Change user's affiliation to member</li>
     * <li>Change user's affiliation to outcast</li>
     * <li>Change user's affiliation to none</li>
     * <li>Change occupant's affiliation to moderator</li>
     * <li>Change occupant's affiliation to participant</li>
     * <li>Change occupant's affiliation to visitor</li>
     * <li>Kick occupants from the room</li>
     * </ul>
     *
     * @param packet the IQ packet sent by an owner or admin of the room.
     * @param role the role of the user that sent the request packet.
     * @throws ForbiddenException If the user is not allowed to perform his request.
     * @throws ConflictException If the desired room nickname is already reserved for the room or
     *                           if the room was going to lose all of its owners.
     * @throws NotAllowedException Thrown if trying to ban an owner or an administrator.
84
     * @throws CannotBeInvitedException If the user being invited as a result of being added to a members-only room still does not have permission
85 86
     */
    public void handleIQ(IQ packet, MUCRole role) throws ForbiddenException, ConflictException,
87
            NotAllowedException, CannotBeInvitedException {
88 89 90 91
        IQ reply = IQ.createResultIQ(packet);
        Element element = packet.getChildElement();

        // Analyze the action to perform based on the included element
92 93 94
        @SuppressWarnings("unchecked")
		List<Element> itemsList = element.elements("item");
        
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
        if (!itemsList.isEmpty()) {
            handleItemsElement(role, itemsList, reply);
        }
        else {
            // An unknown and possibly incorrect element was included in the query
            // element so answer a BAD_REQUEST error
            reply.setChildElement(packet.getChildElement().createCopy());
            reply.setError(PacketError.Condition.bad_request);
        }
        if (reply.getTo() != null) {
            // Send a reply only if the sender of the original packet was from a real JID. (i.e. not
            // a packet generated locally)
            router.route(reply);
        }
    }

    /**
     * Handles packets that includes item elements. Depending on the item's attributes the
     * interpretation of the request may differ. For example, an item that only contains the
     * "affiliation" attribute is requesting the list of participants or members. Whilst if the item
     * contains the affiliation together with a jid means that the client is changing the
     * affiliation of the requested jid.
     *
     * @param senderRole the role of the user that sent the request packet.
     * @param itemsList  the list of items sent by the client.
     * @param reply      the iq packet that will be sent back as a reply to the client's request.
     * @throws ForbiddenException If the user is not allowed to perform his request.
     * @throws ConflictException If the desired room nickname is already reserved for the room or
     *                           if the room was going to lose all of its owners.
     * @throws NotAllowedException Thrown if trying to ban an owner or an administrator.
125
     * @throws CannotBeInvitedException If the user being invited as a result of being added to a members-only room still does not have permission
126
     */
127
    private void handleItemsElement(MUCRole senderRole, List<Element> itemsList, IQ reply)
128
            throws ForbiddenException, ConflictException, NotAllowedException, CannotBeInvitedException {
129
        Element item;
Gaston Dombiak's avatar
Gaston Dombiak committed
130 131
        String affiliation;
        String roleAttribute;
132 133
        boolean hasJID = itemsList.get(0).attributeValue("jid") != null;
        boolean hasNick = itemsList.get(0).attributeValue("nick") != null;
134 135 136
        // Check if the client is requesting or changing the list of moderators/members/etc.
        if (!hasJID && !hasNick) {
            // The client is requesting the list of moderators/members/participants/outcasts
137 138
            for (Object anItem : itemsList) {
                item = (Element) anItem;
139 140 141 142 143 144 145 146 147 148 149 150 151
                affiliation = item.attributeValue("affiliation");
                roleAttribute = item.attributeValue("role");
                // Create the result that will hold an item for each
                // moderator/member/participant/outcast
                Element result = reply.setChildElement("query", "http://jabber.org/protocol/muc#admin");

                Element metaData;
                if ("outcast".equals(affiliation)) {
                    // The client is requesting the list of outcasts
                    if (MUCRole.Affiliation.admin != senderRole.getAffiliation()
                            && MUCRole.Affiliation.owner != senderRole.getAffiliation()) {
                        throw new ForbiddenException();
                    }
152
                    for (JID jid : room.getOutcasts()) {
153 154
                        metaData = result.addElement("item", "http://jabber.org/protocol/muc#admin");
                        metaData.addAttribute("affiliation", "outcast");
155
                        metaData.addAttribute("jid", jid.toString());
156 157
                    }

158
                } else if ("member".equals(affiliation)) {
159 160 161 162 163 164 165
                    // The client is requesting the list of members
                    // In a members-only room members can get the list of members
                    if (!room.isMembersOnly()
                            && MUCRole.Affiliation.admin != senderRole.getAffiliation()
                            && MUCRole.Affiliation.owner != senderRole.getAffiliation()) {
                        throw new ForbiddenException();
                    }
166
                    for (JID jid : room.getMembers()) {
167 168
                        metaData = result.addElement("item", "http://jabber.org/protocol/muc#admin");
                        metaData.addAttribute("affiliation", "member");
169
                        metaData.addAttribute("jid", jid.toString());
170 171 172 173 174 175 176 177 178 179
                        try {
                            List<MUCRole> roles = room.getOccupantsByBareJID(jid);
                            MUCRole role = roles.get(0);
                            metaData.addAttribute("role", role.getRole().toString());
                            metaData.addAttribute("nick", role.getNickname());
                        }
                        catch (UserNotFoundException e) {
                            // Do nothing
                        }
                    }
180
                } else if ("moderator".equals(roleAttribute)) {
181 182 183 184 185 186 187 188
                    // The client is requesting the list of moderators
                    if (MUCRole.Affiliation.admin != senderRole.getAffiliation()
                            && MUCRole.Affiliation.owner != senderRole.getAffiliation()) {
                        throw new ForbiddenException();
                    }
                    for (MUCRole role : room.getModerators()) {
                        metaData = result.addElement("item", "http://jabber.org/protocol/muc#admin");
                        metaData.addAttribute("role", "moderator");
189
                        metaData.addAttribute("jid", role.getUserAddress().toString());
190 191 192
                        metaData.addAttribute("nick", role.getNickname());
                        metaData.addAttribute("affiliation", role.getAffiliation().toString());
                    }
193
                } else if ("participant".equals(roleAttribute)) {
194 195 196 197 198 199 200
                    // The client is requesting the list of participants
                    if (MUCRole.Role.moderator != senderRole.getRole()) {
                        throw new ForbiddenException();
                    }
                    for (MUCRole role : room.getParticipants()) {
                        metaData = result.addElement("item", "http://jabber.org/protocol/muc#admin");
                        metaData.addAttribute("role", "participant");
201
                        metaData.addAttribute("jid", role.getUserAddress().toString());
202 203 204
                        metaData.addAttribute("nick", role.getNickname());
                        metaData.addAttribute("affiliation", role.getAffiliation().toString());
                    }
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
                } else if ("owner".equals(affiliation)) {
                    // The client is requesting the list of owners
                    Element ownerMetaData;
                    MUCRole role;
                    for (JID jid : room.getOwners()) {
                        ownerMetaData = result.addElement("item", "http://jabber.org/protocol/muc#admin");
                        ownerMetaData.addAttribute("affiliation", "owner");
                        ownerMetaData.addAttribute("jid", jid.toBareJID());
                        // Add role and nick to the metadata if the user is in the room
                        try {
                            List<MUCRole> roles = room.getOccupantsByBareJID(jid);
                            role = roles.get(0);
                            ownerMetaData.addAttribute("role", role.getRole().toString());
                            ownerMetaData.addAttribute("nick", role.getNickname());
                        }
                        catch (UserNotFoundException e) {
                            // Do nothing
                        }
                    }
                } else if ("admin".equals(affiliation)) {
                    // The client is requesting the list of admins
                    Element adminMetaData;
                    MUCRole role;
                    for (JID jid : room.getAdmins()) {
                        adminMetaData = result.addElement("item", "http://jabber.org/protocol/muc#admin");
                        adminMetaData.addAttribute("affiliation", "admin");
                        adminMetaData.addAttribute("jid", jid.toBareJID());
                        // Add role and nick to the metadata if the user is in the room
                        try {
                            List<MUCRole> roles = room.getOccupantsByBareJID(jid);
                            role = roles.get(0);
                            adminMetaData.addAttribute("role", role.getRole().toString());
                            adminMetaData.addAttribute("nick", role.getNickname());
                        }
                        catch (UserNotFoundException e) {
                            // Do nothing
                        }
                    }
243
                } else {
244 245 246 247 248 249
                    reply.setError(PacketError.Condition.bad_request);
                }
            }
        }
        else {
            // The client is modifying the list of moderators/members/participants/outcasts
Gaston Dombiak's avatar
Gaston Dombiak committed
250
            JID jid;
251
            String nick;
Gaston Dombiak's avatar
Gaston Dombiak committed
252
            String target;
253
            boolean hasAffiliation = itemsList.get(0).attributeValue("affiliation") !=
254 255 256 257 258 259
                    null;

            // Keep a registry of the updated presences
            List<Presence> presences = new ArrayList<Presence>(itemsList.size());

            // Collect the new affiliations or roles for the specified jids
260
            for (Object anItem : itemsList) {
261
                try {
262
                    item = (Element) anItem;
263 264 265 266 267 268 269
                    target = (hasAffiliation ? item.attributeValue("affiliation") : item
                            .attributeValue("role"));
                    // jid could be of the form "full JID" or "bare JID" depending if we are
                    // going to change a role or an affiliation
                    if (hasJID) {
                        jid = new JID(item.attributeValue("jid"));
                        nick = null;
270
                    } else {
271 272
                        // Get the JID based on the requested nick
                        nick = item.attributeValue("nick");
273
                        jid = room.getOccupant(nick).getUserAddress();
274 275
                    }

276 277 278
                    if ("moderator".equals(target)) {
                        // Add the user as a moderator of the room based on the full JID
                        presences.add(room.addModerator(jid, senderRole));
279 280 281 282
                    } else if ("owner".equals(target)) {
                        presences.addAll(room.addOwner(jid, senderRole));
                    } else if ("admin".equals(target)) {
                        presences.addAll(room.addAdmin(jid, senderRole));
283 284 285 286 287 288 289 290 291 292
                    } else if ("participant".equals(target)) {
                        // Add the user as a participant of the room based on the full JID
                        presences.add(room.addParticipant(jid,
                                item.elementTextTrim("reason"),
                                senderRole));
                    } else if ("visitor".equals(target)) {
                        // Add the user as a visitor of the room based on the full JID
                        presences.add(room.addVisitor(jid, senderRole));
                    } else if ("member".equals(target)) {
                        // Add the user as a member of the room based on the bare JID
293
                        boolean hadAffiliation = room.getAffiliation(jid) != MUCRole.Affiliation.none;
294
                        presences.addAll(room.addMember(jid, nick, senderRole));
295
                        // If the user had an affiliation don't send an invitation. Otherwise
296 297 298
                        // send an invitation if the room is members-only and skipping invites
                       // are not disabled system-wide xmpp.muc.skipInvite
                        if (!skipInvite && !hadAffiliation && room.isMembersOnly()) {
299 300 301 302
                            room.sendInvitation(jid, null, senderRole, null);
                        }
                    } else if ("outcast".equals(target)) {
                        // Add the user as an outcast of the room based on the bare JID
303
                        presences.addAll(room.addOutcast(jid, item.elementTextTrim("reason"), senderRole));
304 305 306
                    } else if ("none".equals(target)) {
                        if (hasAffiliation) {
                            // Set that this jid has a NONE affiliation based on the bare JID
307
                            presences.addAll(room.addNone(jid, senderRole));
308
                        } else {
309 310 311 312 313 314
                            // Kick the user from the room
                            if (MUCRole.Role.moderator != senderRole.getRole()) {
                                throw new ForbiddenException();
                            }
                            presences.add(room.kickOccupant(jid, senderRole.getUserAddress(),
                                    item.elementTextTrim("reason")));
315
                        }
316 317
                    } else {
                        reply.setError(PacketError.Condition.bad_request);
318 319 320 321 322 323 324 325 326 327 328 329 330
                    }
                }
                catch (UserNotFoundException e) {
                    // Do nothing
                }
            }

            // Send the updated presences to the room occupants
            for (Presence presence : presences) {
                room.send(presence);
            }
        }
    }
331
}