Source code: maddany/ChatServlet.java
1 package maddany;
2
3 import java.io.*;
4 import java.net.*;
5 import javax.servlet.*;
6 import javax.servlet.http.*;
7 import java.util.Vector;
8 import java.util.Hashtable;
9 import java.util.Enumeration;
10 import java.util.StringTokenizer;
11 import java.util.Random;
12
13 public class ChatServlet extends HttpServlet implements Analyzer, Wakable {
14 private final String validChars = "abcdefghijklmnopqrstuvwxyz";
15 public final static int IDENT_LENGTH = 3;
16 public final static int TIMEOUT = 120;
17 public final static int PORT = 4242;
18 private Vector pool;
19 private Random r;
20 private ChatListener listener;
21 // private ChatMainFrame win;
22
23 public void init(ServletConfig conf) throws ServletException {
24 super.init(conf);
25 pool = new Vector();
26 r = new Random();
27 listener = new ChatListener(PORT, this);
28
29 // en friche : une fenetre d'administration en local
30 /* win = new ChatMainFrame("mad/chatmin");
31 win.resize(300, 200);
32 win.show(); */
33 }
34
35 public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
36 res.setContentType("text/html");
37 PrintWriter out = res.getWriter();
38 // String local = InetAddress.getLocalHost().getHostAddress();
39 out.println("<html><head><title>Welcome, you lamah</title>\n" +
40 "<body>\n" +
41 // "<p align=center>Ready for some test version ?</p>\n" +
42 "<p align=center>\n" +
43 " <applet code=ChatApplet.class codebase=/maddany/applet width=90% height=90%>\n" +
44 " <param name=port value="+PORT+">\n" +
45 " </applet>\n" +
46 "</p>\n" +
47 "</body>\n" +
48 "</html>");
49 out.close();
50 }
51
52 public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
53 String str;
54 str=req.getReader().readLine();
55 // PrintWriter out = res.getWriter();
56 analyze(str, null);
57 }
58
59 public synchronized String analyze(String toAnalyze, ChatHandler generator) {
60 String command = extractCommand(toAnalyze);
61 String param = extractParam(toAnalyze);
62 if(command==null|param==null) {
63 System.out.println("Missing args");
64 return "SERVER ERROR\r\n"; // "darn you"
65 }
66 String ident = param.length()>=IDENT_LENGTH?param.substring(0, IDENT_LENGTH).toUpperCase():"";
67
68 //IDENT dans ChatHandshaker
69
70 if(command.equals("SEND")) {
71 int idx = param.indexOf(",");
72 if(idx>=0&&generator.isValid(new SequenceAuthenticator(param.substring(0, idx), IDENT_LENGTH))) {
73 // argument : après l'authentificateur
74 String str=param.substring(idx+1, param.length()).replace('<','?'); // /!\
75 if(isValidInput(str)) {
76 broadcast("<"+generator.getNick()+"> "+str, generator);
77 return "SEND OK\r\n";
78 } else {
79 return "SEND GROU\r\n";
80 }
81 } else {
82 return "SEND GROU\r\n";
83 }
84 } else if(command.equals("AINT")) {
85 return "";
86 } else if(command.equals("LOGOUT")){
87 if(generator.isValid(new SequenceAuthenticator(param, IDENT_LENGTH))) {
88 generator.write("LOGOUT BYE\r\n");
89 logUserOut(ident);
90 return "";
91 } else {
92 return "LOGOUT GROU\r\n";
93 }
94 } else if(command.equals("LIST")) {
95 if(generator!=null&&generator.isValid(new SequenceAuthenticator(param, IDENT_LENGTH)))
96 return "LIST "+singleList()+"\r\n";
97 else
98 return "LIST GROU\r\n";
99 }
100 return "BAD CMD,"+command+"\r\n";
101 }
102
103 private synchronized void broadcast(String tosend, ChatHandler generator) {
104 if(tosend.startsWith("<"))
105 internalBroadcast("PUT "+tosend+"\r\n", generator);
106 else
107 internalBroadcast(tosend, generator);
108 }
109
110 private synchronized void internalBroadcast(String tosend, ChatHandler generator) {
111 Enumeration en = poolElements();
112 while(en.hasMoreElements()) {
113 ChatHandler sender = (ChatHandler)en.nextElement();
114 if(sender!=generator) {
115 sender.write(tosend);
116 }
117 }
118 }
119
120 public Vector getThreadPool() {
121 return pool;
122 }
123
124 public void addToPool(Object obj) {
125 System.out.println("Adding "+obj.toString());
126 pool.add(obj);
127 broadcast("LIST "+singleList()+"\r\n", null);
128 }
129
130 public void removeFromPool(Object obj) {
131 System.out.println("Removing "+obj.toString());
132 pool.remove(obj);
133 broadcast("LIST "+singleList()+"\r\n", null);
134 }
135
136 public Enumeration poolElements() {
137 synchronized (pool) {
138 return pool.elements();
139 }
140 }
141
142 public void wakeUp() {}
143
144 public void wakeUp(Object obj) {
145 try {
146 ChatHandler handler = (ChatHandler)obj;
147 handler.start(); //addToPool inclus
148 } catch(ClassCastException e) {
149 e.printStackTrace();
150 }
151 }
152
153 public synchronized String logUserIn(String nick, Socket sck) {
154 String ident = getIdent(nick);
155 if(!containsIdentifier(ident)&&isValidNick(nick)) {
156 long seq = r.nextLong();
157 wakeUp(new ChatHandler(sck, nick, new SequenceAuthenticator(ident, seq), this));
158 return String.valueOf(seq);
159 } else {
160 return null;
161 }
162 }
163
164 private void logUserOut(String ident) {
165 ChatHandler handler = getHandlerByIdent(ident);
166 if(handler!=null) {
167 handler.stop();
168 }
169 }
170
171 public synchronized boolean containsIdentifier(String ident) {
172 Enumeration e = poolElements();
173 while(e.hasMoreElements()) {
174 ChatHandler handler = (ChatHandler)e.nextElement();
175 if(ident.equalsIgnoreCase(handler.getIdentifier()))
176 return true;
177 }
178 return false;
179 }
180
181 public ChatHandler getHandlerByIdent(String ident) {
182 synchronized(pool) {
183 Enumeration e = pool.elements();
184 while(e.hasMoreElements()) {
185 ChatHandler handler = (ChatHandler)e.nextElement();
186 if(ident.equalsIgnoreCase(handler.getIdentifier()))
187 return handler;
188 }
189 return null;
190 }
191 }
192
193 private String getIdent(String nick) {
194 return nick.substring(0, IDENT_LENGTH).toUpperCase();
195 }
196
197 private String singleList() {
198 synchronized(pool) {
199 StringBuffer buf = new StringBuffer();
200 Enumeration en = pool.elements();
201 while(en.hasMoreElements()) {
202 buf.append(((ChatHandler)en.nextElement()).getNick());
203 buf.append(',');
204 }
205 if(buf.length()>0)
206 buf.deleteCharAt(buf.length()-1);
207 return buf.toString();
208 }
209 }
210
211 private boolean isValidInput(String str) {
212 if(str.length()==0|str.length()>1800)
213 return false;
214 else
215 return true;
216 }
217
218 private boolean isValidNick(String nick) {
219 if(nick.length()<3||nick.length()>10)
220 return false;
221 for(int i=0; i<nick.length(); i++) {
222 String ch = String.valueOf(nick.charAt(i));
223 if(validChars.indexOf(ch)<0)
224 return false;
225 }
226 return true;
227 }
228
229 public static String extractCommand(String response) {
230 if(response.indexOf(' ')>0)
231 return response.substring(0, response.indexOf(" "));
232 else
233 return "";
234 }
235
236 public static String extractParam(String response) {
237 if(response.indexOf(' ')>0)
238 return response.substring(response.indexOf(" ")+1, response.length()).trim();
239 else
240 return "";
241 }
242 }
243
244
245 class ChatHandler implements Runnable {
246 private Analyzer parent;
247 private Socket sock;
248 private PrintWriter out;
249 private BufferedReader in;
250 private Thread gate;
251 private String nick;
252 private SequenceAuthenticator auth;
253 private SequenceAuthenticatorManager manager;
254 private InetAddress addr;
255
256 public ChatHandler(Socket sock, String nick, SequenceAuthenticator auth, Analyzer parent) {
257 this.sock = sock;
258 this.parent = parent;
259 this.nick = nick;
260 this.auth = auth;
261 manager = new SequenceAuthenticatorManager();
262 manager.add(auth);
263 addr = sock.getInetAddress();
264 gate = new Thread(this);
265 }
266
267 public void start() {
268 try {
269 if(in==null) {
270 in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
271 out = new PrintWriter(sock.getOutputStream());
272 parent.addToPool(this);
273 gate.start();
274 }
275 } catch(IOException e) {
276 e.printStackTrace();
277 }
278 }
279
280 public void stop() {
281 try {
282 if(gate!=null&&sock!=null) {
283 write("CLOSE LINK\r\n");
284 gate.interrupt();
285 gate = null;
286 sock.close();
287 in.close();
288 out.close();
289 }
290 } catch(IOException e) {
291 e.printStackTrace();
292 } finally {
293 parent.removeFromPool(this);
294 }
295 }
296
297 public void run() {
298 try {
299 while(Thread.currentThread()==gate) {
300 String str = in.readLine();
301 // System.out.println(str+".");
302 String response = parent.analyze(str, this);
303 if(response.length()>0)
304 write(response);
305 }
306 } catch(Exception e) {
307 System.out.println("Error message : "+e.getMessage());
308 } finally {
309 stop();
310 }
311 }
312
313 public synchronized void write(String str) {
314 write(str, true);
315 }
316
317 public synchronized void write(String str, boolean flush) {
318 out.write(str);
319 if(flush)
320 out.flush();
321 }
322
323 public void flush() {
324 out.flush();
325 }
326
327 public String getNick() {
328 return nick;
329 }
330
331 public String getIdentifier() {
332 return auth.getIdentifier();
333 }
334
335 public SequenceAuthenticator getSA() {
336 return auth;
337 }
338
339 public SequenceAuthenticator incrementSA() {
340 auth.incrementSequence(); //updateTime() inclus
341 manager.setElementAt(auth, 0);
342 return auth;
343 }
344
345 public boolean isValid(SequenceAuthenticator auth) {
346 return manager.isValid(auth);
347 }
348
349 public String toString() {
350 StringBuffer buf = new StringBuffer(nick);
351 buf.append(" [");
352 buf.append(addr.getHostAddress());
353 buf.append("]; ");
354 buf.append(auth.toString());
355 return buf.toString();
356 }
357 }