001: /**
002: * $RCSfile$
003: * $Revision: 1125 $
004: * $Date: 2005-03-14 15:59:37 -0300 (Mon, 14 Mar 2005) $
005: *
006: * Copyright (C) 2004 Jive Software. All rights reserved.
007: *
008: * This software is published under the terms of the GNU Public License (GPL),
009: * a copy of which is included in this distribution.
010: */package org.jivesoftware.openfire.muc.spi;
011:
012: import org.dom4j.Element;
013: import org.jivesoftware.openfire.PacketRouter;
014: import org.jivesoftware.openfire.muc.ConflictException;
015: import org.jivesoftware.openfire.muc.ForbiddenException;
016: import org.jivesoftware.openfire.muc.MUCRole;
017: import org.jivesoftware.openfire.muc.NotAllowedException;
018: import org.jivesoftware.openfire.user.UserNotFoundException;
019: import org.xmpp.packet.IQ;
020: import org.xmpp.packet.JID;
021: import org.xmpp.packet.PacketError;
022: import org.xmpp.packet.Presence;
023:
024: import java.util.ArrayList;
025: import java.util.Iterator;
026: import java.util.List;
027:
028: /**
029: * A handler for the IQ packet with namespace http://jabber.org/protocol/muc#admin. This kind of
030: * packets are usually sent by room admins. So this handler provides the necessary functionality
031: * to support administrator requirements such as: managing room members/outcasts/etc., kicking
032: * occupants and banning users.
033: *
034: * @author Gaston Dombiak
035: */
036: public class IQAdminHandler {
037: private LocalMUCRoom room;
038:
039: private PacketRouter router;
040:
041: public IQAdminHandler(LocalMUCRoom chatroom,
042: PacketRouter packetRouter) {
043: this .room = chatroom;
044: this .router = packetRouter;
045: }
046:
047: /**
048: * Handles the IQ packet sent by an owner or admin of the room. Possible actions are:
049: * <ul>
050: * <li>Return the list of participants</li>
051: * <li>Return the list of moderators</li>
052: * <li>Return the list of members</li>
053: * <li>Return the list of outcasts</li>
054: * <li>Change user's affiliation to member</li>
055: * <li>Change user's affiliation to outcast</li>
056: * <li>Change user's affiliation to none</li>
057: * <li>Change occupant's affiliation to moderator</li>
058: * <li>Change occupant's affiliation to participant</li>
059: * <li>Change occupant's affiliation to visitor</li>
060: * <li>Kick occupants from the room</li>
061: * </ul>
062: *
063: * @param packet the IQ packet sent by an owner or admin of the room.
064: * @param role the role of the user that sent the request packet.
065: * @throws ForbiddenException If the user is not allowed to perform his request.
066: * @throws ConflictException If the desired room nickname is already reserved for the room or
067: * if the room was going to lose all of its owners.
068: * @throws NotAllowedException Thrown if trying to ban an owner or an administrator.
069: */
070: public void handleIQ(IQ packet, MUCRole role)
071: throws ForbiddenException, ConflictException,
072: NotAllowedException {
073: IQ reply = IQ.createResultIQ(packet);
074: Element element = packet.getChildElement();
075:
076: // Analyze the action to perform based on the included element
077: List itemsList = element.elements("item");
078: if (!itemsList.isEmpty()) {
079: handleItemsElement(role, itemsList, reply);
080: } else {
081: // An unknown and possibly incorrect element was included in the query
082: // element so answer a BAD_REQUEST error
083: reply
084: .setChildElement(packet.getChildElement()
085: .createCopy());
086: reply.setError(PacketError.Condition.bad_request);
087: }
088: if (reply.getTo() != null) {
089: // Send a reply only if the sender of the original packet was from a real JID. (i.e. not
090: // a packet generated locally)
091: router.route(reply);
092: }
093: }
094:
095: /**
096: * Handles packets that includes item elements. Depending on the item's attributes the
097: * interpretation of the request may differ. For example, an item that only contains the
098: * "affiliation" attribute is requesting the list of participants or members. Whilst if the item
099: * contains the affiliation together with a jid means that the client is changing the
100: * affiliation of the requested jid.
101: *
102: * @param senderRole the role of the user that sent the request packet.
103: * @param itemsList the list of items sent by the client.
104: * @param reply the iq packet that will be sent back as a reply to the client's request.
105: * @throws ForbiddenException If the user is not allowed to perform his request.
106: * @throws ConflictException If the desired room nickname is already reserved for the room or
107: * if the room was going to lose all of its owners.
108: * @throws NotAllowedException Thrown if trying to ban an owner or an administrator.
109: */
110: private void handleItemsElement(MUCRole senderRole, List itemsList,
111: IQ reply) throws ForbiddenException, ConflictException,
112: NotAllowedException {
113: Element item;
114: String affiliation;
115: String roleAttribute;
116: boolean hasJID = ((Element) itemsList.get(0))
117: .attributeValue("jid") != null;
118: boolean hasNick = ((Element) itemsList.get(0))
119: .attributeValue("nick") != null;
120: // Check if the client is requesting or changing the list of moderators/members/etc.
121: if (!hasJID && !hasNick) {
122: // The client is requesting the list of moderators/members/participants/outcasts
123: for (Iterator items = itemsList.iterator(); items.hasNext();) {
124: item = (Element) items.next();
125: affiliation = item.attributeValue("affiliation");
126: roleAttribute = item.attributeValue("role");
127: // Create the result that will hold an item for each
128: // moderator/member/participant/outcast
129: Element result = reply.setChildElement("query",
130: "http://jabber.org/protocol/muc#admin");
131:
132: Element metaData;
133: if ("outcast".equals(affiliation)) {
134: // The client is requesting the list of outcasts
135: if (MUCRole.Affiliation.admin != senderRole
136: .getAffiliation()
137: && MUCRole.Affiliation.owner != senderRole
138: .getAffiliation()) {
139: throw new ForbiddenException();
140: }
141: for (String jid : room.getOutcasts()) {
142: metaData = result.addElement("item",
143: "http://jabber.org/protocol/muc#admin");
144: metaData.addAttribute("affiliation", "outcast");
145: metaData.addAttribute("jid", jid);
146: }
147:
148: } else if ("member".equals(affiliation)) {
149: // The client is requesting the list of members
150: // In a members-only room members can get the list of members
151: if (!room.isMembersOnly()
152: && MUCRole.Affiliation.admin != senderRole
153: .getAffiliation()
154: && MUCRole.Affiliation.owner != senderRole
155: .getAffiliation()) {
156: throw new ForbiddenException();
157: }
158: for (String jid : room.getMembers()) {
159: metaData = result.addElement("item",
160: "http://jabber.org/protocol/muc#admin");
161: metaData.addAttribute("affiliation", "member");
162: metaData.addAttribute("jid", jid);
163: try {
164: List<MUCRole> roles = room
165: .getOccupantsByBareJID(jid);
166: MUCRole role = roles.get(0);
167: metaData.addAttribute("role", role
168: .getRole().toString());
169: metaData.addAttribute("nick", role
170: .getNickname());
171: } catch (UserNotFoundException e) {
172: // Do nothing
173: }
174: }
175: } else if ("moderator".equals(roleAttribute)) {
176: // The client is requesting the list of moderators
177: if (MUCRole.Affiliation.admin != senderRole
178: .getAffiliation()
179: && MUCRole.Affiliation.owner != senderRole
180: .getAffiliation()) {
181: throw new ForbiddenException();
182: }
183: for (MUCRole role : room.getModerators()) {
184: metaData = result.addElement("item",
185: "http://jabber.org/protocol/muc#admin");
186: metaData.addAttribute("role", "moderator");
187: metaData.addAttribute("jid", role
188: .getUserAddress().toString());
189: metaData.addAttribute("nick", role
190: .getNickname());
191: metaData.addAttribute("affiliation", role
192: .getAffiliation().toString());
193: }
194: } else if ("participant".equals(roleAttribute)) {
195: // The client is requesting the list of participants
196: if (MUCRole.Role.moderator != senderRole.getRole()) {
197: throw new ForbiddenException();
198: }
199: for (MUCRole role : room.getParticipants()) {
200: metaData = result.addElement("item",
201: "http://jabber.org/protocol/muc#admin");
202: metaData.addAttribute("role", "participant");
203: metaData.addAttribute("jid", role
204: .getUserAddress().toString());
205: metaData.addAttribute("nick", role
206: .getNickname());
207: metaData.addAttribute("affiliation", role
208: .getAffiliation().toString());
209: }
210: } else {
211: reply.setError(PacketError.Condition.bad_request);
212: }
213: }
214: } else {
215: // The client is modifying the list of moderators/members/participants/outcasts
216: JID jid;
217: String nick;
218: String target;
219: boolean hasAffiliation = ((Element) itemsList.get(0))
220: .attributeValue("affiliation") != null;
221:
222: // Keep a registry of the updated presences
223: List<Presence> presences = new ArrayList<Presence>(
224: itemsList.size());
225:
226: // Collect the new affiliations or roles for the specified jids
227: for (Iterator items = itemsList.iterator(); items.hasNext();) {
228: try {
229: item = (Element) items.next();
230: target = (hasAffiliation ? item
231: .attributeValue("affiliation") : item
232: .attributeValue("role"));
233: // jid could be of the form "full JID" or "bare JID" depending if we are
234: // going to change a role or an affiliation
235: if (hasJID) {
236: jid = new JID(item.attributeValue("jid"));
237: nick = null;
238: } else {
239: // Get the JID based on the requested nick
240: nick = item.attributeValue("nick");
241: jid = room.getOccupant(nick).getUserAddress();
242: }
243:
244: room.lock.writeLock().lock();
245: try {
246: if ("moderator".equals(target)) {
247: // Add the user as a moderator of the room based on the full JID
248: presences.add(room.addModerator(jid,
249: senderRole));
250: } else if ("participant".equals(target)) {
251: // Add the user as a participant of the room based on the full JID
252: presences.add(room.addParticipant(jid, item
253: .elementTextTrim("reason"),
254: senderRole));
255: } else if ("visitor".equals(target)) {
256: // Add the user as a visitor of the room based on the full JID
257: presences.add(room.addVisitor(jid,
258: senderRole));
259: } else if ("member".equals(target)) {
260: // Add the user as a member of the room based on the bare JID
261: boolean hadAffiliation = room
262: .getAffiliation(jid.toBareJID()) != MUCRole.Affiliation.none;
263: presences.addAll(room.addMember(jid
264: .toBareJID(), nick, senderRole));
265: // If the user had an affiliation don't send an invitation. Otherwise
266: // send an invitation if the room is members-only
267: if (!hadAffiliation && room.isMembersOnly()) {
268: room.sendInvitation(jid, null,
269: senderRole, null);
270: }
271: } else if ("outcast".equals(target)) {
272: // Add the user as an outcast of the room based on the bare JID
273: presences.addAll(room.addOutcast(jid
274: .toBareJID(), item
275: .elementTextTrim("reason"),
276: senderRole));
277: } else if ("none".equals(target)) {
278: if (hasAffiliation) {
279: // Set that this jid has a NONE affiliation based on the bare JID
280: presences.addAll(room.addNone(jid
281: .toBareJID(), senderRole));
282: } else {
283: // Kick the user from the room
284: if (MUCRole.Role.moderator != senderRole
285: .getRole()) {
286: throw new ForbiddenException();
287: }
288: presences
289: .add(room
290: .kickOccupant(
291: jid,
292: senderRole
293: .getUserAddress(),
294: item
295: .elementTextTrim("reason")));
296: }
297: } else {
298: reply
299: .setError(PacketError.Condition.bad_request);
300: }
301: } finally {
302: room.lock.writeLock().unlock();
303: }
304: } catch (UserNotFoundException e) {
305: // Do nothing
306: }
307: }
308:
309: // Send the updated presences to the room occupants
310: for (Presence presence : presences) {
311: room.send(presence);
312: }
313: }
314: }
315: }
|