KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > muc > spi > MUCUserImpl


1 /**
2  * $RCSfile: MUCUserImpl.java,v $
3  * $Revision: 1.23 $
4  * $Date: 2005/07/12 21:40:16 $
5  *
6  * Copyright (C) 2004 Jive Software. All rights reserved.
7  *
8  * This software is published under the terms of the GNU Public License (GPL),
9  * a copy of which is included in this distribution.
10  */

11
12 package org.jivesoftware.messenger.muc.spi;
13
14 import java.util.*;
15 import java.util.concurrent.ConcurrentHashMap JavaDoc;
16
17 import org.dom4j.Element;
18
19 import org.jivesoftware.messenger.muc.*;
20 import org.jivesoftware.util.*;
21 import org.jivesoftware.messenger.*;
22 import org.jivesoftware.messenger.auth.UnauthorizedException;
23 import org.jivesoftware.messenger.user.UserAlreadyExistsException;
24 import org.jivesoftware.messenger.user.UserNotFoundException;
25 import org.xmpp.packet.*;
26
27 /**
28  * Implementation of MUCUser. There will be a MUCUser per user that is connected to one or more
29  * rooms. A MUCUser contains a collection of MUCRoles for each room where the user has joined.
30  *
31  * @author Gaston Dombiak
32  */

33 public class MUCUserImpl implements MUCUser {
34
35     /** The chat server this user belongs to. */
36     private MultiUserChatServer server;
37
38     /** Real system XMPPAddress for the user. */
39     private JID realjid;
40
41     /** Table: key roomName.toLowerCase(); value MUCRole. */
42     private Map JavaDoc<String JavaDoc, MUCRole> roles = new ConcurrentHashMap JavaDoc<String JavaDoc, MUCRole>();
43
44     /** Deliver packets to users. */
45     private PacketRouter router;
46
47     /**
48      * Time of last packet sent.
49      */

50     private long lastPacketTime;
51
52     /**
53      * Create a new chat user.
54      *
55      * @param chatserver the server the user belongs to.
56      * @param packetRouter the router for sending packets from this user.
57      * @param jid the real address of the user
58      */

59     MUCUserImpl(MultiUserChatServerImpl chatserver, PacketRouter packetRouter, JID jid) {
60         this.realjid = jid;
61         this.router = packetRouter;
62         this.server = chatserver;
63     }
64
65     public long getID() {
66         return -1;
67     }
68
69     public MUCRole getRole(String JavaDoc roomName) throws NotFoundException {
70         MUCRole role = roles.get(roomName.toLowerCase());
71         if (role == null) {
72             throw new NotFoundException(roomName);
73         }
74         return role;
75     }
76
77     public Iterator<MUCRole> getRoles() {
78         return Collections.unmodifiableCollection(roles.values()).iterator();
79     }
80
81     public void removeRole(String JavaDoc roomName) {
82         roles.remove(roomName.toLowerCase());
83     }
84
85     public long getLastPacketTime() {
86         return lastPacketTime;
87     }
88
89     /**
90      * Generate a conflict packet to indicate that the nickname being requested/used is already in
91      * use by another user.
92      *
93      * @param packet the packet to be bounced.
94      */

95     private void sendErrorPacket(Packet packet, PacketError.Condition error) {
96         if (packet instanceof IQ) {
97             IQ reply = IQ.createResultIQ((IQ) packet);
98             reply.setChildElement(((IQ) packet).getChildElement().createCopy());
99             reply.setError(error);
100             router.route(reply);
101         }
102         else {
103             Packet reply = packet.createCopy();
104             reply.setError(error);
105             reply.setFrom(packet.getTo());
106             reply.setTo(packet.getFrom());
107             router.route(reply);
108         }
109     }
110
111     public JID getAddress() {
112         return realjid;
113     }
114
115     public void process(Packet packet) throws UnauthorizedException, PacketException {
116         if (packet instanceof IQ) {
117             process((IQ)packet);
118         }
119         else if (packet instanceof Message) {
120             process((Message)packet);
121         }
122         else if (packet instanceof Presence) {
123             process((Presence)packet);
124         }
125     }
126
127     /**
128      * This method does all packet routing in the chat server. Packet routing is actually very
129      * simple:
130      *
131      * <ul>
132      * <li>Discover the room the user is talking to (server packets are dropped)</li>
133      * <li>If the room is not registered and this is a presence "available" packet, try to join the
134      * room</li>
135      * <li>If the room is registered, and presence "unavailable" leave the room</li>
136      * <li>Otherwise, rewrite the sender address and send to the room.</li>
137      * </ul>
138      *
139      * @param packet The packet to route.
140      */

141     public void process(Message packet) {
142         // Ignore messages of type ERROR sent to a room
143
if (Message.Type.error == packet.getType()) {
144             return;
145         }
146         lastPacketTime = System.currentTimeMillis();
147         JID recipient = packet.getTo();
148         String JavaDoc group = recipient.getNode();
149         if (group == null) {
150             // Ignore packets to the groupchat server
151
// In the future, we'll need to support TYPE_IQ queries to the server for MUC
152
Log.info(LocaleUtils.getLocalizedString("muc.error.not-supported") + " "
153                     + packet.toString());
154         }
155         else {
156             MUCRole role = roles.get(group.toLowerCase());
157             if (role == null) {
158                 if (server.hasChatRoom(group)) {
159                     boolean declinedInvitation = false;
160                     Element userInfo = null;
161                     if (Message.Type.normal == packet.getType()) {
162                         // An user that is not an occupant could be declining an invitation
163
userInfo = packet.getChildElement(
164                                 "x", "http://jabber.org/protocol/muc#user");
165                         if (userInfo != null
166                                 && userInfo.element("decline") != null) {
167                             // A user has declined an invitation to a room
168
// WARNING: Potential fraud if someone fakes the "from" of the
169
// message with the JID of a member and sends a "decline"
170
declinedInvitation = true;
171                         }
172                     }
173                     if (declinedInvitation) {
174                         Element info = userInfo.element("decline");
175                         server.getChatRoom(group).sendInvitationRejection(
176                             new JID(info.attributeValue("to")),
177                             info.elementTextTrim("reason"),
178                             packet.getFrom());
179                     }
180                     else {
181                         // The sender is not an occupant of the room
182
sendErrorPacket(packet, PacketError.Condition.not_acceptable);
183                     }
184                 }
185                 else {
186                     // The sender is not an occupant of a NON-EXISTENT room!!!
187
sendErrorPacket(packet, PacketError.Condition.recipient_unavailable);
188                 }
189             }
190             else {
191                 // Check and reject conflicting packets with conflicting roles
192
// In other words, another user already has this nickname
193
if (!role.getChatUser().getAddress().equals(packet.getFrom())) {
194                     sendErrorPacket(packet, PacketError.Condition.conflict);
195                 }
196                 else {
197                     try {
198                         if (packet.getSubject() != null && packet.getSubject().trim().length() > 0
199                                 && Message.Type.groupchat == packet.getType()) {
200                             // An occupant is trying to change the room's subject
201
role.getChatRoom().changeSubject(packet, role);
202
203                         }
204                         else {
205                             // An occupant is trying to send a private, send public message,
206
// invite someone to the room or reject an invitation
207
Message.Type type = packet.getType();
208                             String JavaDoc resource = packet.getTo().getResource();
209                             if (resource == null || resource.trim().length() == 0) {
210                                 resource = null;
211                             }
212                             if (resource == null && Message.Type.groupchat == type) {
213                                 // An occupant is trying to send a public message
214
role.getChatRoom().sendPublicMessage(packet, role);
215                             }
216                             else if (resource != null
217                                     && (Message.Type.chat == type || Message.Type.normal == type)) {
218                                 // An occupant is trying to send a private message
219
role.getChatRoom().sendPrivateMessage(packet, role);
220                             }
221                             else if (resource == null && Message.Type.normal == type) {
222                                 // An occupant could be sending an invitation or declining an
223
// invitation
224
Element userInfo = packet.getChildElement(
225                                     "x",
226                                     "http://jabber.org/protocol/muc#user");
227                                 // Real real real UGLY TRICK!!! Will and MUST be solved when
228
// persistence will be added. Replace locking with transactions!
229
MUCRoomImpl room = (MUCRoomImpl) role.getChatRoom();
230                                 if (userInfo != null && userInfo.element("invite") != null) {
231                                     // An occupant is sending an invitation
232
Element info = userInfo.element("invite");
233
234                                     // Add the user as a member of the room if the room is
235
// members only
236
if (room.isMembersOnly()) {
237                                         room.lock.writeLock().lock();
238                                         try {
239                                             room.addMember(info.attributeValue("to"), null, role);
240                                         }
241                                         finally {
242                                             room.lock.writeLock().unlock();
243                                         }
244                                     }
245                                     // Try to keep the list of extensions sent together with the
246
// message invitation. These extensions will be sent to the
247
// invitee.
248
List<Element> extensions = new ArrayList<Element>(packet
249                                             .getElement().elements());
250                                     extensions.remove(userInfo);
251
252                                     // Send the invitation to the user
253
room.sendInvitation(new JID(info.attributeValue("to")),
254                                             info.elementTextTrim("reason"), role, extensions);
255                                 }
256                                 else if (userInfo != null
257                                         && userInfo.element("decline") != null) {
258                                     // An occupant has declined an invitation
259
Element info = userInfo.element("decline");
260                                     room.sendInvitationRejection(new JID(info.attributeValue("to")),
261                                             info.elementTextTrim("reason"), packet.getFrom());
262                                 }
263                                 else {
264                                     sendErrorPacket(packet, PacketError.Condition.bad_request);
265                                 }
266                             }
267                             else {
268                                 sendErrorPacket(packet, PacketError.Condition.bad_request);
269                             }
270                         }
271                     }
272                     catch (ForbiddenException e) {
273                         sendErrorPacket(packet, PacketError.Condition.forbidden);
274                     }
275                     catch (NotFoundException e) {
276                         sendErrorPacket(packet, PacketError.Condition.recipient_unavailable);
277                     }
278                     catch (ConflictException e) {
279                         sendErrorPacket(packet, PacketError.Condition.conflict);
280                     }
281                 }
282             }
283         }
284     }
285
286     public void process(IQ packet) {
287         // Ignore IQs of type ERROR sent to a room
288
if (IQ.Type.error == packet.getType()) {
289             return;
290         }
291         lastPacketTime = System.currentTimeMillis();
292         JID recipient = packet.getTo();
293         String JavaDoc group = recipient.getNode();
294         if (group == null) {
295             // Ignore packets to the groupchat server
296
// In the future, we'll need to support TYPE_IQ queries to the server for MUC
297
Log.info(LocaleUtils.getLocalizedString("muc.error.not-supported") + " "
298                     + packet.toString());
299         }
300         else {
301             MUCRole role = roles.get(group.toLowerCase());
302             if (role == null) {
303                 // TODO: send error message to user (can't send packets to group you haven't
304
// joined)
305
}
306             else {
307                 // Check and reject conflicting packets with conflicting roles
308
// In other words, another user already has this nickname
309
if (!role.getChatUser().getAddress().equals(packet.getFrom())) {
310                     sendErrorPacket(packet, PacketError.Condition.conflict);
311                 }
312                 else {
313                     try {
314                         Element query = packet.getElement().element("query");
315                         if (query != null &&
316                                 "http://jabber.org/protocol/muc#owner".equals(query.getNamespaceURI())) {
317                             role.getChatRoom().getIQOwnerHandler().handleIQ(packet, role);
318                         }
319                         else if (query != null &&
320                                 "http://jabber.org/protocol/muc#admin".equals(query.getNamespaceURI())) {
321                             role.getChatRoom().getIQAdminHandler().handleIQ(packet, role);
322                         }
323                         else {
324                             sendErrorPacket(packet, PacketError.Condition.bad_request);
325                         }
326                     }
327                     catch (ForbiddenException e) {
328                         sendErrorPacket(packet, PacketError.Condition.forbidden);
329                     }
330                     catch (ConflictException e) {
331                         sendErrorPacket(packet, PacketError.Condition.conflict);
332                     }
333                     catch (NotAllowedException e) {
334                         sendErrorPacket(packet, PacketError.Condition.not_allowed);
335                     }
336                 }
337             }
338         }
339     }
340
341     public void process(Presence packet) {
342         // Ignore presences of type ERROR sent to a room
343
if (Presence.Type.error == packet.getType()) {
344             return;
345         }
346         lastPacketTime = System.currentTimeMillis();
347         JID recipient = packet.getTo();
348         String JavaDoc group = recipient.getNode();
349         if (group == null) {
350             if (Presence.Type.unavailable == packet.getType()) {
351                 server.removeUser(packet.getFrom());
352             }
353         }
354         else {
355             MUCRole role = roles.get(group.toLowerCase());
356             if (role == null) {
357                 // If we're not already in a room, we either are joining it or it's not
358
// properly addressed and we drop it silently
359
if (recipient.getResource() != null
360                         && recipient.getResource().trim().length() > 0) {
361                     if (packet.isAvailable()) {
362                         try {
363                             // Get or create the room
364
MUCRoom room = server.getChatRoom(group, packet.getFrom());
365                             // User must support MUC in order to create a room
366
Element mucInfo = packet.getChildElement("x",
367                                     "http://jabber.org/protocol/muc");
368                             HistoryRequest historyRequest = null;
369                             String JavaDoc password = null;
370                             // Check for password & requested history if client supports MUC
371
if (mucInfo != null) {
372                                 password = mucInfo.elementTextTrim("password");
373                                 if (mucInfo.element("history") != null) {
374                                     historyRequest = new HistoryRequest(mucInfo);
375                                 }
376                             }
377                             // The user joins the room
378
role = room.joinRoom(recipient.getResource().trim(),
379                                     password,
380                                     historyRequest,
381                                     this,
382                                     packet.createCopy());
383                             roles.put(group.toLowerCase(), role);
384                             // If the client that created the room is non-MUC compliant then
385
// unlock the room thus creating an "instant" room
386
if (mucInfo == null && room.isLocked() && !room.isManuallyLocked()) {
387                                 room.unlock(role);
388                             }
389                         }
390                         catch (UnauthorizedException e) {
391                             sendErrorPacket(packet, PacketError.Condition.not_authorized);
392                         }
393                         catch (ServiceUnavailableException e) {
394                             sendErrorPacket(packet, PacketError.Condition.service_unavailable);
395                         }
396                         catch (UserAlreadyExistsException e) {
397                             sendErrorPacket(packet, PacketError.Condition.conflict);
398                         }
399                         catch (RoomLockedException e) {
400                             sendErrorPacket(packet, PacketError.Condition.recipient_unavailable);
401                         }
402                         catch (ForbiddenException e) {
403                             sendErrorPacket(packet, PacketError.Condition.forbidden);
404                         }
405                         catch (RegistrationRequiredException e) {
406                             sendErrorPacket(packet, PacketError.Condition.registration_required);
407                         }
408                         catch (ConflictException e) {
409                             sendErrorPacket(packet, PacketError.Condition.conflict);
410                         }
411                         catch (NotAcceptableException e) {
412                             sendErrorPacket(packet, PacketError.Condition.not_acceptable);
413                         }
414                     }
415                     else {
416                         // TODO: send error message to user (can't send presence to group you
417
// haven't joined)
418
}
419                 }
420                 else {
421                     if (packet.isAvailable()) {
422                         // A resource is required in order to join a room
423
sendErrorPacket(packet, PacketError.Condition.bad_request);
424                     }
425                     // TODO: send error message to user (can't send packets to group you haven't
426
// joined)
427
}
428             }
429             else {
430                 // Check and reject conflicting packets with conflicting roles
431
// In other words, another user already has this nickname
432
if (!role.getChatUser().getAddress().equals(packet.getFrom())) {
433                     sendErrorPacket(packet, PacketError.Condition.conflict);
434                 }
435                 else {
436                     if (Presence.Type.unavailable == packet.getType()) {
437                         try {
438                             roles.remove(group.toLowerCase());
439                             role.getChatRoom().leaveRoom(role.getNickname());
440                         }
441                         catch (UserNotFoundException e) {
442                             // Do nothing since the users has already left the room
443
}
444                         catch (Exception JavaDoc e) {
445                             Log.error(e);
446                         }
447                     }
448                     else {
449                         try {
450                             String JavaDoc resource = (recipient.getResource() == null
451                                     || recipient.getResource().trim().length() == 0 ? null
452                                     : recipient.getResource().trim());
453                             if (resource == null
454                                     || role.getNickname().equalsIgnoreCase(resource)) {
455                                 // Occupant has changed his availability status
456
role.setPresence(packet.createCopy());
457                                 role.getChatRoom().send(role.getPresence().createCopy());
458                             }
459                             else {
460                                 // Occupant has changed his nickname. Send two presences
461
// to each room occupant
462

463                                 // Check if occupants are allowed to change their nicknames
464
if (!role.getChatRoom().canChangeNickname()) {
465                                     sendErrorPacket(packet, PacketError.Condition.not_acceptable);
466                                 }
467                                 // Answer a conflic error if the new nickname is taken
468
else if (role.getChatRoom().hasOccupant(resource)) {
469                                     sendErrorPacket(packet, PacketError.Condition.conflict);
470                                 }
471                                 else {
472                                     // Send "unavailable" presence for the old nickname
473
Presence presence = role.getPresence().createCopy();
474                                     // Switch the presence to OFFLINE
475
presence.setType(Presence.Type.unavailable);
476                                     presence.setStatus(null);
477                                     // Add the new nickname and status 303 as properties
478
Element frag = presence.getChildElement("x",
479                                             "http://jabber.org/protocol/muc#user");
480                                     frag.element("item").addAttribute("nick", resource);
481                                     frag.addElement("status").addAttribute("code", "303");
482                                     role.getChatRoom().send(presence);
483
484                                     // Send availability presence for the new nickname
485
String JavaDoc oldNick = role.getNickname();
486                                     role.setPresence(packet.createCopy());
487                                     role.changeNickname(resource);
488                                     role.getChatRoom().nicknameChanged(oldNick, resource);
489                                     role.getChatRoom().send(role.getPresence().createCopy());
490                                 }
491                             }
492                         }
493                         catch (Exception JavaDoc e) {
494                             Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
495                         }
496                     }
497                 }
498             }
499         }
500     }
501 }
Popular Tags