View Javadoc
1   /*
2    * Copyright (c) 2002-2017, Mairie de Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.chat.service;
35  
36  import fr.paris.lutece.plugins.chat.business.ChatEntry;
37  import fr.paris.lutece.plugins.chat.business.ChatRoom;
38  import fr.paris.lutece.plugins.chat.business.ChatUser;
39  import fr.paris.lutece.plugins.chat.business.RoomList;
40  
41  /**
42   * Titre : ChatService
43   * Description : Class that provides basic services for chat clinkts
44   * Copyright :    Copyright (c) 2001
45   * Soci?t? :      Mairie de Paris
46   * @author        SePD
47   * @version 1.0
48   */
49  import fr.paris.lutece.portal.service.util.AppPropertiesService;
50  
51  import java.text.MessageFormat;
52  import java.text.SimpleDateFormat;
53  
54  import java.util.Date;
55  import java.util.Enumeration;
56  import java.util.Locale;
57  
58  import javax.servlet.http.HttpServletRequest;
59  import javax.servlet.http.HttpSession;
60  
61  
62  /**
63   * This class manages chat users and rooms.
64   */
65  public final class ChatService implements Runnable
66  {
67      ////////////////////////////////////////////////////////////////////////////
68      // Constants
69      // chat.properties
70      private static final String PROPERTY_MAX_INACTIVITY = "chat.users.max.inactivity.seconds";
71      private static final String PROPERTY_LANGUAGE = "chat.language";
72      private static final String UNDEFINED = "undefined";
73      private static final String PROPERTY_DEF_ADMIN_PASSWORD = "chatadmin";
74      private static final String PROPERTY_DEF_BGCOLOR = "DDDDDD";
75      private static final String PROPERTY_DEF_BTBGCOLOR = "555555";
76      private static final String PROPERTY_DEF_BTFGCOLOR = "FFFFFF";
77      private static final String PROPERTY_DEF_FDBGCOLOR = "FFFFFF";
78      private static final String PROPERTY_FLOOD_DELAY_SECONDS = "chat.flood.delay.seconds";
79      private static final String PROPERTY_FLOOD_MAX_DATA_SIZE = "chat.flood.max.data.size";
80      private static final int PROPERTY_FLOOD_DELAY_SECONDS_DEF = 5;
81      private static final int PROPERTY_FLOOD_MAX_DATA_SIZE_DEF = 300;
82      private static final String PROPERTY_FLOOD_BOT_NAME = "chat.flood.bot.name";
83      private static final String PROPERTY_FLOOD_BOT_NAME_DEF = "ChatSupervisor";
84      private static final String PROPERTY_FLOOD_BOT_MESSAGE = "chat.flood.bot.message";
85      private static final String PROPERTY_FLOOD_BOT_MESSAGE_DEF = "Excess flood";
86  
87      // IRC like commands
88      private static final String COMMAND_MODE = "/MODE";
89      private static final String COMMAND_WHOIS = "/WHOIS";
90      private static final String COMMAND_KICK = "/KICK";
91      private static final String COMMAND_ME = "/ME";
92      private static final String COMMAND_NICK = "/NICK";
93      private static final String COMMAND_MSG = "/MSG";
94      private static final String COMMAND_TOPIC = "/TOPIC";
95      private static final String COMMAND_AWAY = "/AWAY";
96      private static final String COMMAND_PART = "/PART";
97      private static final String COMMAND_QUIT = "/QUIT";
98  
99      // Specific commands
100     private static final String COMMAND_OP = "/OP";
101     private static final String COMMAND_BANLIST = "/BANLIST";
102 
103     // Notification messages
104     private static final String MSG_OP = "op";
105     private static final String MSG_QUIT = "quit";
106     private static final String MSG_ENTER = "enter";
107     private static final String MSG_TOPIC = "topic";
108     private static final String MSG_AWAY = "away";
109     private static final String MSG_COMEBACK = "comeback";
110     private static final String MSG_NICK = "nick";
111     private static final String MSG_KICK = "kick";
112     private static final String MSG_KICKED = "kicked";
113     private static final String MSG_BAN = "ban";
114     private static final String MSG_BANNED = "banned";
115     private static final String MSG_DEBAN = "deban";
116     private static final String MSG_MODE = "mode";
117     private static final String MSG_COMMAND_DENIED = "command.denied";
118     private static final String MSG_COMMAND_INVALID_PARMS = "command.invalid.params";
119     private static final String MSG_COMMAND_INVALID_USER = "command.invalid.user";
120     private static final String MSG_COMMAND_UNKOWN = "command.unkown";
121     private static final String MSG_INVALID_NICK = "invalid.nick";
122     private static final String MSG_EXIT = "exit";
123     private static final String MSG_BANNED_LIST_TITLE = "msg.banned.list.title";
124     private static final String MSG_BANNED_LIST_TABLE = "msg.banned.list.table";
125     private static final String MSG_USERS_INFOS_TITLE = "msg.users.infos.title";
126     private static final String MSG_USERS_INFOS_IP = "msg.users.infos.ip";
127     private static final String MSG_USERS_INFOS_LAST_ACCESS = "msg.users.infos.last.access";
128     private static final String MSG_USERS_INFOS_ENTRANCE = "msg.users.infos.entrance";
129 
130     // Static variables
131     private static RoomList _roomList;
132     private Thread _timer;
133 
134     // Constructor ( private  singleton )
135 
136     /**
137      * Creates a new ChatService object.
138      */
139     private ChatService(  )
140     {
141         // Create a timer to remove disconnected users
142         _timer = new Thread( this );
143         _timer.start(  );
144     }
145 
146     /**
147      * Room entering process
148      *
149      * @param request The Http request
150      * @return One of the error codes defined in the ChatRoom class
151      * @see fr.paris.lutece.plugins.chat.business.ChatRoom
152          *
153      */
154     public static synchronized int doEnterRoom( HttpServletRequest request )
155     {
156         // Get the room posted in the form
157         String strRoomName = request.getParameter( ChatConstantes.PARAM_ROOM );
158 
159         if ( ( strRoomName == null ) || ( strRoomName.length(  ) == 0 ) )
160         {
161             return ChatRoom.INVALID_ROOM;
162         }
163 
164         // Attach the room to the user's session
165         HttpSession session = request.getSession(  );
166         session.setAttribute( ChatConstantes.ATTRIBUTE_ROOM_NAME, strRoomName );
167 
168         // R?cup?re le pseudo envoy? dans la requete du formulaire
169         String strNickname = request.getParameter( ChatConstantes.PARAM_NICKNAME );
170 
171         // Attache le pseudo ? la session
172         session.setAttribute( ChatConstantes.ATTRIBUTE_NICKNAME, strNickname );
173 
174         // Cr?e l'appUser et l'ajoute au salon
175         ChatUser user = new ChatUser( strNickname );
176         user.setIpAddress( request.getRemoteAddr(  ) );
177         user.setHostName( request.getRemoteHost(  ) );
178         user.setLastAccessTime( new Date(  ) );
179 
180         ChatRoom room = getRoom( request );
181 
182         if ( room == null )
183         {
184             return ChatRoom.INVALID_ROOM;
185         }
186 
187         int nError = room.addUser( user );
188 
189         if ( nError != ChatRoom.USER_ADDED )
190         {
191             if ( nError == ChatRoom.USER_ALREADY_EXISTS )
192             {
193                 while ( nError == ChatRoom.USER_ALREADY_EXISTS )
194                 {
195                     user.setNickname( user.getNickname(  ) + "_" );
196                     nError = room.addUser( user );
197                 }
198             }
199             else
200             {
201                 return nError;
202             }
203         }
204 
205         room.addChatEntry( new ChatEntry( formatMsg( MSG_ENTER, strNickname ) ), null );
206 
207         return ChatRoom.USER_ADDED;
208     }
209 
210     ////////////////////////////////////////////////////////////////////////////
211     // Rooms management
212 
213     /**
214      * Get the room attached to the session
215      *
216      * @param request the servlet request
217      * @return the room attached to the session
218      */
219     public static ChatRoom getRoom( HttpServletRequest request )
220     {
221         String strRoomName = request.getParameter( ChatConstantes.PARAM_ROOM );
222 
223         if ( strRoomName == null )
224         {
225             HttpSession session = request.getSession(  );
226             strRoomName = (String) session.getAttribute( ChatConstantes.ATTRIBUTE_ROOM_NAME );
227         }
228 
229         ChatRoom room = _roomList.getRoom( strRoomName );
230 
231         if ( room == null )
232         {
233             return null;
234         }
235 
236         return room;
237     }
238 
239     /**
240      * Returns the rooms list
241      *
242      * @return the rooms ist as a RoomList object
243      */
244     public static RoomList getRoomList(  )
245     {
246         return _roomList;
247     }
248 
249     /**
250      * Initialize rooms
251     
252     
253          */
254     public static void initRooms(  )
255     {
256         // Create rooms
257         _roomList = createRooms(  );
258     }
259 
260     /**
261      * Create rooms from the properties file
262      * @return The created rooms list as a RoomList object
263      */
264     private static RoomList createRooms(  )
265     {
266         RoomList roomList = new RoomList(  );
267         String strRoomName;
268         String strRoomNameKey;
269         String strRoomDescription;
270         String strRoomDescriptionKey;
271 
272         int i = 1;
273 
274         while ( true )
275         {
276             strRoomNameKey = "chat.room" + i + ".name";
277             strRoomName = AppPropertiesService.getProperty( strRoomNameKey, UNDEFINED );
278 
279             if ( strRoomName.equals( UNDEFINED ) )
280             {
281                 break;
282             }
283 
284             strRoomDescriptionKey = "chat.room" + i + ".description";
285             strRoomDescription = AppPropertiesService.getProperty( strRoomDescriptionKey, UNDEFINED );
286 
287             ChatRoom room = new ChatRoom( strRoomName, strRoomDescription );
288 
289             // read other room attributes
290             room.setAdminPassword( AppPropertiesService.getProperty( "chat.room" + i + ".admin.password",
291                     PROPERTY_DEF_ADMIN_PASSWORD ) );
292             room.setBgColor( AppPropertiesService.getProperty( "chat.room" + i + ".bgcolor", PROPERTY_DEF_BGCOLOR ) );
293             room.setButtonBgColor( AppPropertiesService.getProperty( "chat.room" + i + ".btbgcolor",
294                     PROPERTY_DEF_BTBGCOLOR ) );
295             room.setButtonFgColor( AppPropertiesService.getProperty( "chat.room" + i + ".btfgcolor",
296                     PROPERTY_DEF_BTFGCOLOR ) );
297             room.setFieldBgColor( AppPropertiesService.getProperty( "chat.room" + i + ".fdbgcolor",
298                     PROPERTY_DEF_FDBGCOLOR ) );
299             roomList.addRoom( room );
300             i++;
301         }
302 
303         return roomList;
304     }
305 
306     ////////////////////////////////////////////////////////////////////////////
307     // Users management
308 
309     /**
310      * Remove all users with no activity since a given time
311      *
312      * @param room The room to remove users from
313      */
314     private void clearUsers( ChatRoom room )
315     {
316         // Parcours la liste des appUsers pour voir ceux qui ne sont plus actifs
317         Date dateCurrent = new Date(  );
318         int nMaxInactivity = AppPropertiesService.getPropertyInt( PROPERTY_MAX_INACTIVITY, 60 );
319         Enumeration e = room.getUsers(  );
320 
321         while ( e.hasMoreElements(  ) )
322         {
323             ChatUser user = (ChatUser) e.nextElement(  );
324             Date lastActivity = user.getLastAccessTime(  );
325 
326             if ( ( dateCurrent.getTime(  ) - lastActivity.getTime(  ) ) > ( nMaxInactivity * 1000 ) )
327             {
328                 room.removeUser( user.getNickname(  ) );
329                 room.addChatEntry( new ChatEntry( formatMsg( MSG_QUIT, user.getNickname(  ) ) ), null );
330             }
331         }
332     }
333 
334     /**
335      * Ban flooders from a room.
336      * @param room The room to ban users from
337      */
338     private void banFlooders( ChatRoom room )
339     {
340         // Parcours la liste des appUsers pour voir ceux qui ne sont plus actifs
341         Enumeration e = room.getUsers(  );
342         long lSeconds = AppPropertiesService.getPropertyInt( PROPERTY_FLOOD_DELAY_SECONDS,
343                 PROPERTY_FLOOD_DELAY_SECONDS_DEF );
344         int nMaxSize = AppPropertiesService.getPropertyInt( PROPERTY_FLOOD_MAX_DATA_SIZE,
345                 PROPERTY_FLOOD_MAX_DATA_SIZE_DEF );
346         String strFloodBotName = AppPropertiesService.getProperty( PROPERTY_FLOOD_BOT_NAME, PROPERTY_FLOOD_BOT_NAME_DEF );
347         String strFloodBotMessage = AppPropertiesService.getProperty( PROPERTY_FLOOD_BOT_MESSAGE,
348                 PROPERTY_FLOOD_BOT_MESSAGE_DEF );
349 
350         while ( e.hasMoreElements(  ) )
351         {
352             ChatUser u = (ChatUser) e.nextElement(  );
353 
354             if ( u.getSentDataSizeSince( lSeconds ) > nMaxSize )
355             {
356                 u.kick( formatMsg( MSG_KICKED, strFloodBotName, u.getNickname(  ), strFloodBotMessage ) );
357                 room.addChatEntry( new ChatEntry( formatMsg( MSG_KICK, strFloodBotName, u.getNickname(  ),
358                             strFloodBotMessage ) ), null );
359                 room.banUser( u.getNickname(  ), formatMsg( MSG_BANNED ) );
360                 room.addChatEntry( new ChatEntry( formatMsg( MSG_BAN, strFloodBotName, u.getNickname(  ), "" ) ), null );
361             }
362         }
363     }
364 
365     /**
366      * Scan rooms for no activity users in order to remove them.
367      * Use Runnable implementation
368      */
369     public void run(  )
370     {
371         Thread me = Thread.currentThread(  );
372 
373         while ( _timer == me )
374         {
375             try
376             {
377                 Thread.sleep( 1000 );
378             }
379             catch ( InterruptedException e )
380             {
381             }
382 
383             RoomList list = getRoomList(  );
384             Enumeration e = list.getRooms(  );
385 
386             while ( e.hasMoreElements(  ) )
387             {
388                 ChatRoom room = (ChatRoom) e.nextElement(  );
389                 clearUsers( room );
390                 banFlooders( room );
391             }
392         }
393     }
394 
395     /**
396      * Get nickname attached to the request or to user's session
397      *
398      * @param request the servlet request
399          * @return The nickname
400      */
401     public static String getNickname( HttpServletRequest request )
402     {
403         String strNickname = request.getParameter( ChatConstantes.PARAM_NICKNAME );
404 
405         if ( strNickname == null )
406         {
407             HttpSession session = request.getSession(  );
408             strNickname = (String) session.getAttribute( ChatConstantes.ATTRIBUTE_NICKNAME );
409         }
410 
411         return strNickname;
412     }
413 
414     ////////////////////////////////////////////////////////////////////////////
415     // Message management
416 
417     /**
418      *
419      * @param request the HttpServlet request
420      * @return if a new message is processed
421      */
422     public static boolean newMessage( HttpServletRequest request )
423     {
424         ChatRoom room = ChatService.getRoom( request );
425 
426         if ( room == null )
427         {
428             return false;
429         }
430 
431         String strNickname = ChatService.getNickname( request );
432         ChatUser user = room.getUser( strNickname );
433 
434         if ( ( user == null ) || user.isKicked(  ) )
435         {
436             return false;
437         }
438 
439         // if the user was away, he's no longer away
440         if ( user.isAway(  ) )
441         {
442             user.setAway( false );
443             room.addChatEntry( user, new ChatEntry( formatMsg( MSG_COMEBACK, strNickname ) ), null );
444         }
445 
446         String strMessage = request.getParameter( ChatConstantes.PARAM_MESSAGE );
447 
448         if ( ( strMessage != null ) && ( strMessage.length(  ) != 0 ) )
449         {
450             Message message = new Message( strMessage );
451 
452             if ( message.isCommand(  ) )
453             {
454                 parseCommand( message, room, strNickname );
455             }
456             else
457             {
458                 room.addChatEntry( user, new ChatEntry( strNickname, strMessage, ChatEntry.TYPE_MESSAGE ), null );
459             }
460         }
461 
462         return true;
463     }
464 
465     /**
466      * Commands parsing
467      *
468      * @param message The message
469      * @param room The room
470      * @param strNickname The nickname
471      */
472     private static void parseCommand( Message message, ChatRoom room, String strNickname )
473     {
474         ChatUser user = room.getUser( strNickname );
475 
476         if ( message.isCommand( COMMAND_AWAY ) )
477         {
478             setAway( room, user, message.getArgs(  ) );
479 
480             return;
481         }
482         else if ( message.isCommand( COMMAND_QUIT ) )
483         {
484             quit( room, user, message.getArgs(  ) );
485 
486             return;
487         }
488         else if ( message.isCommand( COMMAND_PART ) )
489         {
490             quit( room, user, "" );
491 
492             return;
493         }
494 
495         if ( user.getMode(  ) == ChatUser.MODE_OP )
496         {
497             if ( message.isCommand( COMMAND_MODE ) )
498             {
499                 changeUserMode( room, user, message.getArg1(  ), message.getArg2(  ) );
500             }
501             else if ( message.isCommand( COMMAND_WHOIS ) )
502             {
503                 whoisUser( room, user, message.getArgs(  ) );
504             }
505             else if ( message.isCommand( COMMAND_KICK ) )
506             {
507                 kickUser( room, user, message.getArg1(  ), message.getArg2(  ) );
508             }
509             else if ( message.isCommand( COMMAND_NICK ) )
510             {
511                 changeNickname( room, user, message.getArgs(  ) );
512             }
513             else if ( message.isCommand( COMMAND_TOPIC ) )
514             {
515                 setTopic( room, user, message.getArgs(  ) );
516             }
517             else if ( message.isCommand( COMMAND_MSG ) )
518             {
519                 sendPrivateMessage( room, user, message.getArg1(  ), message.getArg2(  ) );
520             }
521             else if ( message.isCommand( COMMAND_ME ) )
522             {
523                 room.addChatEntry( user,
524                     new ChatEntry( strNickname, message.getArgs(  ), ChatEntry.TYPE_NOTIFICATION ), null );
525             }
526             else if ( message.isCommand( COMMAND_BANLIST ) )
527             {
528                 banlist( room, user );
529             }
530             else
531             {
532                 room.addChatEntry( user, new ChatEntry( formatMsg( MSG_COMMAND_UNKOWN, strNickname ) ),
533                     room.getUser( strNickname ) );
534             }
535         }
536         else
537         {
538             if ( message.isCommand( COMMAND_OP ) )
539             {
540                 String strAdminPassword = message.getArgs(  );
541 
542                 if ( strAdminPassword.equalsIgnoreCase( room.getAdminPassword(  ) ) )
543                 {
544                     user.setMode( ChatUser.MODE_OP );
545                     room.addChatEntry( new ChatEntry( strNickname, formatMsg( MSG_OP, strNickname ),
546                             ChatEntry.TYPE_NOTIFICATION ), null );
547                 }
548             }
549             else
550             {
551                 room.addChatEntry( user, new ChatEntry( formatMsg( MSG_COMMAND_DENIED, strNickname ) ), user );
552             }
553         }
554     }
555 
556     /**
557      * Changes the user mode
558      *
559      *@param room The chat room
560      *@param userOperator  the chat room operator
561      *@param strMode The user mode
562      *@param strUser The user
563      */
564     private static void changeUserMode( ChatRoom room, ChatUser userOperator, String strMode, String strUser )
565     {
566         int nMode = getNewUserMode( strMode.toUpperCase(  ) );
567         ChatUser user = null;
568 
569         switch ( nMode )
570         {
571             case ChatUser.MODE_USER:
572             case ChatUser.MODE_VOICE:
573             case ChatUser.MODE_OP:
574                 user = checkCommandUser( room, strUser, userOperator );
575 
576                 if ( user != null )
577                 {
578                     user.setMode( nMode );
579                     room.addChatEntry( new ChatEntry( formatMsg( MSG_MODE, userOperator.getNickname(  ),
580                                 user.getNickname(  ), "" ) ), null );
581                 }
582 
583                 break;
584 
585             case ChatUser.MODE_BAN:
586                 user = checkCommandUser( room, strUser, userOperator );
587 
588                 if ( user != null )
589                 {
590                     room.banUser( user.getNickname(  ), formatMsg( MSG_BANNED ) );
591                     room.addChatEntry( new ChatEntry( formatMsg( MSG_BAN, userOperator.getNickname(  ),
592                                 user.getNickname(  ), "" ) ), null );
593                 }
594 
595                 break;
596 
597             case ChatUser.MODE_DEBAN:
598                 room.debanUser( strUser );
599                 room.addChatEntry( new ChatEntry( formatMsg( MSG_DEBAN, "", strUser, "" ) ), userOperator );
600 
601                 break;
602 
603             default:
604                 room.addChatEntry( new ChatEntry( formatMsg( MSG_COMMAND_INVALID_PARMS, "", user.getNickname(  ), "" ) ),
605                     userOperator );
606         }
607     }
608 
609     /**
610      * Returns the mode number from a string
611      *
612      *@param strMode  the mode string
613      *@return the mode number
614      */
615     private static int getNewUserMode( String strMode )
616     {
617         if ( strMode.equals( "+O" ) )
618         {
619             return ChatUser.MODE_OP;
620         }
621 
622         if ( strMode.equals( "+V" ) )
623         {
624             return ChatUser.MODE_VOICE;
625         }
626 
627         if ( strMode.equals( "-O" ) || strMode.equals( "-V" ) )
628         {
629             return ChatUser.MODE_USER;
630         }
631 
632         if ( strMode.equals( "+B" ) )
633         {
634             return ChatUser.MODE_BAN;
635         }
636 
637         if ( strMode.equals( "-B" ) )
638         {
639             return ChatUser.MODE_DEBAN;
640         }
641 
642         return -1;
643     }
644 
645     /**
646      * Returns informations about a user
647      *
648      *@param room The chat room
649      *@param userOperator The user's operator
650      *@param strUser The name of the user
651      */
652     private static void whoisUser( ChatRoom room, ChatUser userOperator, String strUser )
653     {
654         ChatUser user = checkCommandUser( room, strUser, userOperator );
655 
656         if ( user != null )
657         {
658             String strMessage = AppPropertiesService.getProperty( MSG_USERS_INFOS_TITLE ) + user.getNickname(  ) +
659                 "\n" + AppPropertiesService.getProperty( MSG_USERS_INFOS_IP ) + " (" + user.getIpAddress(  ) + ") " +
660                 user.getHostName(  ) + "\n" + AppPropertiesService.getProperty( MSG_USERS_INFOS_LAST_ACCESS ) +
661                 user.getLastAccessTime(  ).toString(  ) + "\n" +
662                 AppPropertiesService.getProperty( MSG_USERS_INFOS_ENTRANCE ) +
663                 new Date( user.getJoinTime(  ) ).toString(  );
664             room.addChatEntry( new ChatEntry( "", strMessage, ChatEntry.TYPE_NOTIFICATION ), userOperator );
665         }
666     }
667 
668     /**
669      * Kicks a user out of a chat room
670      *
671      * @param room The chat room
672      * @param userOperator The operator
673      * @param strUser The name of the user
674      * @param strComment A string comment
675      */
676     private static void kickUser( ChatRoom room, ChatUser userOperator, String strUser, String strComment )
677     {
678         ChatUser user = checkCommandUser( room, strUser, userOperator );
679 
680         if ( user != null )
681         {
682             room.addChatEntry( new ChatEntry( formatMsg( MSG_KICK, userOperator.getNickname(  ), strUser, strComment ) ),
683                 null );
684             user.kick( formatMsg( MSG_KICKED, userOperator.getNickname(  ), strUser, strComment ) );
685         }
686     }
687 
688     /**
689      * Sends a private message from the chat operator to a user
690      *
691      * @param room The chat room
692      * @param userOperator The chat operator
693      * @param strUser The user name
694      * @param strMessage The message
695      */
696     private static void sendPrivateMessage( ChatRoom room, ChatUser userOperator, String strUser, String strMessage )
697     {
698         ChatUser user = checkCommandUser( room, strUser, userOperator );
699 
700         if ( user != null )
701         {
702             String strSender = "<" + userOperator.getNickname(  ) + ">";
703             room.addChatEntry( user, new ChatEntry( strSender, strMessage, ChatEntry.TYPE_MESSAGE ), user );
704             room.addChatEntry( userOperator, new ChatEntry( strSender, strMessage, ChatEntry.TYPE_MESSAGE ),
705                 userOperator );
706         }
707     }
708 
709     /**
710      * Changes a user nickname
711      *
712      * @param room The chat room
713      * @param user The user
714      * @param strNewNickname The new nickname
715      */
716     private static void changeNickname( ChatRoom room, ChatUser user, String strNewNickname )
717     {
718         String strNickname = user.getNickname(  );
719 
720         if ( room.changePseudo( strNickname, strNewNickname ) != ChatRoom.USER_ADDED )
721         {
722             room.addChatEntry( user, new ChatEntry( formatMsg( MSG_INVALID_NICK, strNewNickname ) ), user );
723         }
724         else
725         {
726             room.addChatEntry( user, new ChatEntry( formatMsg( MSG_NICK, strNickname, "", strNewNickname ) ), null );
727         }
728     }
729 
730     /**
731      * Sets a user away
732      *
733      * @param room The chat room
734      * @param user The user
735      * @param strComment A string comment
736      */
737     private static void setAway( ChatRoom room, ChatUser user, String strComment )
738     {
739         if ( user.getMode(  ) == ChatUser.MODE_OP )
740         {
741             user.setMode( ChatUser.MODE_VOICE );
742         }
743 
744         user.setAway( strComment );
745         room.addChatEntry( user, new ChatEntry( formatMsg( MSG_AWAY, user.getNickname(  ), "", strComment ) ), null );
746     }
747 
748     /**
749      * Allows a user to leave the chat room
750      *
751      * @param room The chat room
752      * @param user The user
753      * @param strComment A comment
754      */
755     private static void quit( ChatRoom room, ChatUser user, String strComment )
756     {
757         room.addChatEntry( new ChatEntry( formatMsg( MSG_QUIT, user.getNickname(  ), "", strComment ) ), null );
758         user.kick( formatMsg( MSG_EXIT ) );
759     }
760 
761     /**
762      * Sets the topic of the chat room
763      *
764      * @param room The chat room
765      * @param user The user
766      * @param strTopic The topic
767      */
768     private static void setTopic( ChatRoom room, ChatUser user, String strTopic )
769     {
770         room.setDescription( strTopic );
771         room.addChatEntry( new ChatEntry( formatMsg( MSG_TOPIC, user.getNickname(  ), "", strTopic ) ), null );
772     }
773 
774     /**
775      * Adds an entry with the ban list
776      *
777      * @param room The chat room
778      * @param userOperator The chat operator
779      */
780     private static void banlist( ChatRoom room, ChatUser userOperator )
781     {
782         Enumeration e = room.getBannedUsers(  );
783         StringBuffer strList = new StringBuffer(  );
784         strList.append( AppPropertiesService.getProperty( MSG_BANNED_LIST_TITLE ) + "\n" );
785         strList.append( AppPropertiesService.getProperty( MSG_BANNED_LIST_TABLE ) + "\n" );
786 
787         SimpleDateFormat formatter = new SimpleDateFormat( "dd'/'MM'/'yyyy' 'HH':'mm", Locale.FRANCE );
788 
789         while ( e.hasMoreElements(  ) )
790         {
791             ChatUser user = (ChatUser) e.nextElement(  );
792             strList.append( user.getIpAddress(  ) );
793             strList.append( "    " );
794             strList.append( formatter.format( user.getLastAccessTime(  ) ) );
795             strList.append( "        " );
796             strList.append( user.getNickname(  ) );
797             strList.append( "\n" );
798         }
799 
800         room.addChatEntry( new ChatEntry( "", strList.toString(  ), ChatEntry.TYPE_NOTIFICATION ), userOperator );
801     }
802 
803     /**
804      * Checks the user validity
805      *
806      * @param room The chat room
807      * @param strUser The user name
808      * @param userOperator The chat operator
809          *
810          * @return The object corespondig to the user name, null if the name is
811          * invalid
812      */
813     private static ChatUser checkCommandUser( ChatRoom room, String strUser, ChatUser userOperator )
814     {
815         ChatUser user = room.getUser( strUser );
816 
817         if ( user == null )
818         {
819             room.addChatEntry( new ChatEntry( formatMsg( MSG_COMMAND_INVALID_USER, userOperator.getNickname(  ),
820                         strUser, "" ) ), userOperator );
821         }
822 
823         return user;
824     }
825 
826     /**
827      * Formats a message
828      *
829      * @param strMessageName The message name
830      * @param strNickname The nickname
831      * @param strUser The name of the user
832      * @param strComment A comment
833      * @return The formated message
834      */
835     private static String formatMsg( String strMessageName, String strNickname, String strUser, String strComment )
836     {
837         String strRessource = "chat.msg." + strMessageName;
838         String strLanguage = AppPropertiesService.getProperty( PROPERTY_LANGUAGE, "" );
839 
840         if ( !strLanguage.equals( "" ) )
841         {
842             strRessource += ( "." + strLanguage );
843         }
844 
845         String strPattern = AppPropertiesService.getProperty( strRessource );
846         MessageFormat msgFormat = new MessageFormat( strPattern );
847         Object[] msgArgs = { strNickname, strUser, strComment };
848 
849         return msgFormat.format( msgArgs );
850     }
851 
852     /**
853      * Formats a message
854      *
855      * @param strMessageName The message name
856      * @param strNickname The nickname
857      * @return The formated message
858      */
859     private static String formatMsg( String strMessageName, String strNickname )
860     {
861         return formatMsg( strMessageName, strNickname, "", "" );
862     }
863 
864     /**
865      * Formats a message
866      *
867      * @param strMessageName The message name
868      * @return The formated message
869      */
870     private static String formatMsg( String strMessageName )
871     {
872         String strRessource = "chat.msg." + strMessageName;
873         String strLanguage = AppPropertiesService.getProperty( PROPERTY_LANGUAGE, "" );
874 
875         if ( !strLanguage.equals( "" ) )
876         {
877             strRessource += ( "." + strLanguage );
878         }
879 
880         return AppPropertiesService.getProperty( strRessource, "" );
881     }
882 }
883 
884 
885 ////////////////////////////////////////////////////////////////////////////////
886 // Class Message
887 
888 /**
889  * Utility class for message management :
890  * <li>Extract commands and arguments from messages
891  */
892 class Message
893 {
894     private String _strCommand = ""; // command found in the message
895     private String _strArgs = ""; // data found after the command
896     private String _strArg1 = ""; // first argument found after the command
897     private String _strArg2 = ""; // data found after the first argument
898     private boolean _bCommand;// indicate whether the message is a command or not
899 
900     /**
901      * Creates a new Message object.
902      *
903      * @param strMessage The text of the message
904      */
905     Message( String strMessage )
906     {
907         parseMessage( strMessage );
908     }
909 
910     /**
911      * Parse a message
912          *
913      * @param strMessage The message text
914      */
915     private void parseMessage( String strMessage )
916     {
917         if ( !strMessage.startsWith( "/" ) )
918         {
919             return;
920         }
921 
922         _bCommand = true;
923 
924         int nPos = strMessage.indexOf( " " );
925 
926         if ( nPos != -1 )
927         {
928             _strCommand = strMessage.substring( 0, nPos ).toUpperCase(  );
929             _strArgs = strMessage.substring( nPos + 1, strMessage.length(  ) );
930             nPos = _strArgs.indexOf( " " );
931 
932             if ( nPos != -1 )
933             {
934                 _strArg1 = _strArgs.substring( 0, nPos );
935                 _strArg2 = _strArgs.substring( nPos + 1, _strArgs.length(  ) );
936             }
937             else
938             {
939                 _strArg1 = _strArgs;
940             }
941         }
942         else
943         {
944             _strCommand = strMessage.toUpperCase(  );
945         }
946     }
947 
948     /**
949      * @return true if the message contains a command.
950      */
951     boolean isCommand(  )
952     {
953         return _bCommand;
954     }
955 
956     /**
957      * Returns true if the message contains the specified command.
958      * @param strCommand The command to check
959      * @return true if the message contains the specified command
960      */
961     boolean isCommand( String strCommand )
962     {
963         return _strCommand.equals( strCommand );
964     }
965 
966     /**
967      * Return the command in upper case
968          *
969          * @return The command in upper case
970      */
971     String getCommand(  )
972     {
973         return _strCommand;
974     }
975 
976     /**
977      * Return all data found after the command
978          *
979          * @return The data
980      */
981     String getArgs(  )
982     {
983         return _strArgs;
984     }
985 
986     /**
987      * Return first argument found after the command
988          *
989          * @return The argument
990      */
991     String getArg1(  )
992     {
993         return _strArg1;
994     }
995 
996     /**
997      * Return data found after the first argument
998          *
999          * @return The data
1000      */
1001     String getArg2(  )
1002     {
1003         return _strArg2;
1004     }
1005 }