001: /*- ClientConnection.java -----------------------------------------+
002: | |
003: | Copyright (C) 2002-2003 Joseph Monti, LlamaChat |
004: | countjoe@users.sourceforge.net |
005: | http://www.42llamas.com/LlamaChat/ |
006: | |
007: | This program is free software; you can redistribute it and/or |
008: | modify it under the terms of the GNU General Public License |
009: | as published by the Free Software Foundation; either version 2 |
010: | of the License, or (at your option) any later version |
011: | |
012: | This program is distributed in the hope that it will be useful, |
013: | but WITHOUT ANY WARRANTY; without even the implied warranty of |
014: | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
015: | GNU General Public License for more details. |
016: | |
017: | A copy of the GNU General Public License may be found in the |
018: | installation directory named "GNUGPL.txt" |
019: | |
020: +-----------------------------------------------------------------+
021: */
022:
023: package server;
024:
025: import javax.net.ssl.*;
026: import java.io.BufferedInputStream;
027: import java.io.BufferedOutputStream;
028: import java.io.ObjectOutputStream;
029: import java.io.ObjectInputStream;
030: import java.io.IOException;
031: import java.lang.ClassNotFoundException;
032: import common.*;
033: import common.sd.*;
034:
035: /* -------------------- JavaDoc Information ----------------------*/
036: /**
037: * A threaded connection class to maintain the conneciton to the client
038: * a new ClientConnection must be create for each new client
039: * @author Joseph Monti <a href="mailto:countjoe@users.sourceforge.net">countjoe@users.sourceforge.net</a>
040: * @version 0.8
041: */
042: public class ClientConnection implements Runnable, SocketConnection {
043: private LlamaChatServer server;
044: private SSLSocket socket;
045: public String name;
046: public String ip;
047: public String channel;
048:
049: private ObjectInputStream in;
050: private ObjectOutputStream out;
051: private boolean finalized;
052: private boolean admin;
053:
054: private MessageQueue queue;
055:
056: ClientConnection(LlamaChatServer serv, SSLSocket sock) {
057: try {
058: name = null;
059: finalized = false;
060: admin = false;
061: channel = serv.channels.defaultChannel;
062: server = serv;
063: socket = sock;
064: ip = sock.getInetAddress().getHostName();
065: out = new ObjectOutputStream(sock.getOutputStream());
066: in = new ObjectInputStream(sock.getInputStream());
067: queue = new MessageQueue(this );
068: new Thread(this ).start();
069: } catch (IOException e) {
070: server.log(this , "failed connection");
071: server.connectingUsers.remove(this );
072: }
073: }
074:
075: /**
076: * Method to read an object from the client
077: * @return returns the SocketData object recieved
078: */
079: private SocketData readObject() {
080: try {
081: return (SocketData) in.readObject();
082: } catch (IOException e) {
083: return null;
084: } catch (ClassNotFoundException e) {
085: return null;
086: }
087: }
088:
089: /**
090: * Method to write an object to the server via MessageQueue
091: * @param sd A SocketData object to be sent
092: */
093: public void writeObject(Object obj) {
094: queue.enqueue(obj);
095: }
096:
097: /**
098: * Method to actually write an object to the server
099: * @param sd A SocketData object to be sent
100: */
101: public void _writeObject(Object obj) {
102: try {
103: out.writeObject(obj);
104: out.flush();
105: } catch (IOException e) {
106: close();
107: }
108: }
109:
110: /**
111: * closes the connection to the client
112: */
113: public void close() {
114: server.kill(this );
115: try {
116: socket.close();
117: } catch (IOException e) {
118: e.printStackTrace();
119: }
120: }
121:
122: /**
123: * utility method to retrieve the admin status
124: * @return true if admin
125: */
126: public boolean isAdmin() {
127: return admin;
128: }
129:
130: // -- Interface methods -- \\
131: /**
132: * receives server configurations
133: * @param type the type of data being sent
134: * @param obj the object being sent
135: */
136: public void serverCap(char type, Object obj) {
137: // does nothing ... eventually will be able to configure from client
138: }
139:
140: /**
141: * used to finalize the conneciton to the client.
142: * when a user connects he must send a SD_UserAdd to tell
143: * the server the requested name
144: * @param username the name of the client
145: */
146: public void userAdd(String username) {
147: if (username == null) {
148: writeObject(new SD_Error("no username recieved"));
149: return;
150: }
151: if (name == null && !finalized) {
152: name = username;
153: finalized = server.finalizeUser(username, this );
154: if (!finalized) {
155: name = null;
156: writeObject(new SD_Kick(null));
157: }
158: } else {
159: writeObject(new SD_Error("already recieved name: " + name));
160: }
161: }
162:
163: /**
164: * used by a client to attain administrative status
165: * @param password the password provided by the client
166: */
167: public void adminAdd(String password) {
168: if (!server.allowAdmin) {
169: writeObject(new SD_Error(
170: "admins are not allow on this server"));
171: } else if (password.equals(server.adminPass)) {
172: admin = true;
173: server.broadcast(new SD_AdminAdd(name), null, channel);
174: } else {
175: writeObject(new SD_Error(
176: "incorrect administrative password"));
177: }
178: }
179:
180: /**
181: * Tells the server that the client wishes to disconnect
182: * @param username the name of the user
183: */
184: public void userDel(String username) {
185: close();
186: }
187:
188: /**
189: * used to rename the client
190: * <i>ADD USERNAME VALIDITY CHECK</a>
191: * @param on the old name of the client
192: * @param nn the new (requested) name of the client
193: */
194: public void rename(String on, String nn) {
195: if (server.connectedUsers.containsKey(nn)) {
196: writeObject(new SD_Error("username already exists"));
197: //writeObject(new SD_Rename(nn, name));
198: } else {
199: server.connectedUsers.remove(name);
200: server.connectedUsers.put(nn, this );
201: server.broadcast(new SD_Rename(name, nn), nn, channel);
202: server.updateUserExport();
203: name = nn;
204: }
205: }
206:
207: /**
208: * used to kick a user; eventually IP banning will be an added
209: * option (called ban), but will use SD_Kick
210: * @param username the name of the user to kicko
211: */
212: public void kick(String username) {
213: if (admin) {
214: ClientConnection cc = (ClientConnection) server.connectedUsers
215: .get(username);
216: if (cc == null) {
217: writeObject(new SD_Error("user " + username
218: + " does not exist"));
219: } else {
220: server.sendTo(new SD_Error("You have been kicked by "
221: + name), username);
222: cc.close();
223: }
224: } else {
225: writeObject(new SD_Error(
226: "Couldn't verify administrative status"));
227: }
228: }
229:
230: /**
231: * recieves a channel change
232: * @param nc true for a new channel, if the channel exists an error is
233: sent back to the user, otherwise the channel is created
234: and a recursive call is made to switch the user to
235: the new channel; if false it checks the validity of the
236: request and move the user
237: * @param n the name of the channel
238: * @param p password for the channel, if one
239: */
240: public void channel(boolean nc, String n, String p) {
241: if (nc) {
242: String reason;
243: if ((reason = server.newChannel(n, p, this )) == null) {
244: server.log(this , "channel " + n + " created");
245: channel(false, n, p);
246: } else {
247: writeObject(new SD_Error(reason));
248: }
249: } else {
250: if (server.channels.channelExists(n)) {
251: if (channel.equals(n)) {
252: writeObject(new SD_Error("Already a member of " + n));
253: writeObject(new SD_Channel(false, null, null));
254: } else if (!server.channels.userAdd(n, p)) {
255: writeObject(new SD_Error(
256: "invalid passphrase or none "
257: + "provided, use \\join " + n
258: + " <password>"));
259: writeObject(new SD_Channel(false, null, null));
260: } else {
261: server.broadcast(new SD_UserDel(name), name,
262: channel);
263: if (server.channels.userDel(channel)) {
264: server.broadcast(new SD_Channel(true, channel,
265: null), null);
266: server.chatLog(this , false);
267: server.log(this , "channel " + channel
268: + " removed");
269: }
270: channel = n;
271: server.broadcast(new SD_UserAdd(name), name,
272: channel);
273: if (admin) {
274: server.broadcast(new SD_AdminAdd(name), name,
275: channel);
276: }
277:
278: writeObject(new SD_Channel(false, channel, null));
279: server.sendUserList(this );
280: server.updateUserExport();
281: }
282: } else {
283: writeObject(new SD_Error(n + " does not exist"));
284: writeObject(new SD_Channel(false, null, null));
285: }
286: }
287: }
288:
289: /**
290: * recives a chat message from the user
291: * @param username the name of the user, not used in this case
292: (it should be null)
293: * @param message the messsage sent by the user
294: */
295: public void chat(String username, String message) {
296: if (finalized) {
297: server.chatLog(this , message);
298: server.broadcast(new SD_Chat(name, message), name, channel);
299: } else {
300: writeObject(new SD_Error("connection not confirmed"));
301: }
302: }
303:
304: /**
305: * recives a private message from the user
306: * @param username the name of the user to whom the message is sent
307: * @param message the messsage sent by the user
308: */
309: public void private_msg(String username, String message) {
310: if (finalized) {
311: server.sendTo(new SD_Private(name, message), username);
312: } else {
313: writeObject(new SD_Error("connection not confirmed"));
314: }
315: }
316:
317: /**
318: * whispers to a user
319: * @param username the name of the user that the whisper is to be sent
320: * @param message the message that is to be whispered
321: */
322: public void whisper(String username, String message) {
323: if (finalized) {
324: server.sendTo(new SD_Whisper(name, message), username);
325: } else {
326: writeObject(new SD_Error("connection not confirmed"));
327: }
328: }
329:
330: /**
331: * used to control the chat logging status
332: * @param start true to start logging, false to stop
333: */
334: public void chatLog(boolean start) {
335: if (admin) {
336: if (server.chatLog(this , start)) {
337: writeObject(new SD_Log(start));
338: } else {
339: writeObject(new SD_Error("unable to modify log for "
340: + channel));
341: }
342: } else {
343: writeObject(new SD_Error(
344: "Could not verify administrative status"));
345: }
346: }
347:
348: /**
349: * recieves an error from the client
350: * @param err the error to report
351: */
352: public void error(String err) {
353: server.log(this , err);
354: }
355:
356: // -- Interface methods -- \\
357:
358: /**
359: * Method to implement runnable, listens
360: * for incoming objects
361: */
362: public void run() {
363: SocketData sd;
364: while ((sd = readObject()) != null) {
365: sd.performAction(this);
366: }
367: close();
368: }
369: }
|