//******************************************************************** // server.java //******************************************************************** // Implements the server side of a Java based client/server chat system. // // Version History: // 1.00 M. Ayers 2/3/97 // Base version, contains comments and 'looks nice' // // 1.01 M. Ayers 3/30/97 // Added filtering, tested all functions, no bugs found. // // 1.02 M. Ayers 4/15/97 // Multi Server, allows one server to connect to another as a // client. // // 1.03 M. Ayers 4/20/97 // Tuned up server, checked for errors. //******************************************************************** import java.io.*; import sun.net.*; import java.net.*; class server extends NetworkServer //Uses Sun's networking classes. { String user[]; //Base global declarations. DataInputStream net_input[]; PrintStream net_output[]; String channel_list[]; int channel_count[]; String user_type[]; String user_channel[]; String user_filter[]; String other_server; int other_port; String temp_string; //Global counters. static int channel_counter = 0; static int perm_channel = 0; static int update = 0; static int maximum = 100; static int next_user = 0; static int port = 1111; Socket network_client; // Networking Declarations. DataInputStream server_input; PrintStream server_output; boolean connected = false; server_writer w; public static void main(String args[]) //Start server. { new server(); } public server() //Begin server main code. { user = new String[maximum]; //Declare all arrays. net_input = new DataInputStream[maximum]; net_output = new PrintStream[maximum]; channel_list = new String[maximum]; channel_count = new int[maximum]; user_type = new String[maximum]; user_channel = new String[maximum]; user_filter = new String[maximum]; try { read_info(); //Read startup information from file. } catch (IOException e) { System.out.println("ERROR: startup.channels file not available."); } try { read_config(); //Read startup information from file. } catch (IOException e) { System.out.println("ERROR: server.config file not available."); } try { startServer(port); //Bind server to port. } catch (Exception e) { System.out.println("ERROR: Unable to start server."); return; } if (other_server != null) { if (connect()) //Check for connection creation. { connected = true; w = new server_writer(this); w.start(); } else { System.out.println("Warning: Not able to connect to server " + other_server); } } System.out.println("Message: Server ready, waiting for users..."); update = 1; while(true) //Infinite loop. { if(update == 1) //Has server information been updated? { for (int i=perm_channel; i < channel_counter; I++) //Check for empty channels. { if (channel_count[i] == 0) //Clear empty channels. { for(int j = 0; j < maximum; j++) { if ( user[j] != null) { if (user_type[j].equals("L")) { write_net_output(net_output[j], "REMOVECHANNEL&" + channel_list[i] ); } } } for (int k=i; k < channel_counter ; k++) { channel_list[k] = channel_list[k+1]; channel_count[k] = channel_count[k+1]; } channel_counter--; } } for (int i=0; i < maximum ; i++) { if (user[i] != null) { System.out.println("Info: User - " + user[i] + " Channel - " + user_channel[i] + " Type - " + user_type[i]); } } for (int i=0; i < maximum ; i++) { if (user[i] != null && user_type[i].equals("L")) { for (int j=0; j < channel_counter; j++) { write_net_output(net_output[i], "ADDCHANNEL&" + channel_list[j] + "&" + channel_count[j] ); } } } try { write_info(); //Write server status (for use by CGI). } catch (IOException e) { System.out.println("ERROR: Problem with .list files."); } update = 0; } // End update. try { Thread.sleep(1000); } catch (InterruptedException e) { ; } } // End while. } // End server. public void serviceRequest() //Handle user connections. { DataInputStream input = new DataInputStream(clientInput); //Get client IO streams. PrintStream output = clientOutput; boolean new_channel = true; for (int i=0;i < maximum ;i++ ) //Look for empty slot in table { if(user[i] == null) { next_user = i; break; } } net_input[next_user] = input; net_output[next_user] = output; user[next_user] = read_net_input(input).trim(); user_channel[next_user] = read_net_input(input).trim(); user_type[next_user] = "L"; if (user_channel[next_user].equals("server")) { user_type[next_user] = "S"; } else { if (connected) { write_net_output(server_output, "ADDUSER&" + user[next_user] + "&" + user_channel[next_user]); } for (int k=0; k < channel_counter ; k++) { if (channel_list[k].equals(user_channel[next_user])) { channel_count[k]++; new_channel = false; break; } } if (new_channel) { channel_list[channel_counter] = user_channel[next_user]; channel_count[channel_counter] = 1; channel_counter++; for(int i = 0; i < maximum; i++) { if (user[i] != null && user_type[i].equals("L")) { write_net_output(net_output[i], "ADDCHANNEL&" + user_channel[next_user]); } } } } System.out.println("Info: " + user[next_user] + " connected on channel " + user_channel[next_user] + " Index - " + next_user); (new reader(this, next_user)).start(); //Start thread to handle user request. int c = next_user; while (user[c] != null) { try { Thread.sleep(5000); } catch (InterruptedException e) { ; } } } // End serviceRequest. String read_net_input(DataInputStream input) //Read network input, one line at a time. { try { return input.readLine(); } catch (IOException e) { return null; } } void write_net_output(PrintStream output, String string) //Write to network, one line at a time. { output.println(string); output.flush(); } void read_info() throws IOException //Read startup info. { RandomAccessFile f = new RandomAccessFile("startup.channels", "r"); //Open startup.channels file. String string; int i =0; string = f.readLine(); while(string != null) { channel_list[i] = string.trim(); System.out.println("Info: Permanent channel - " + channel_list[i]); i++; string = f.readLine(); } channel_counter = i; perm_channel = i; f.close(); } // End read_info. void write_info() throws IOException //Write status files for CGI. { RandomAccessFile file1 = new RandomAccessFile("channel.list", "rw"); //Open files. RandomAccessFile file2 = new RandomAccessFile("user.list", "rw"); RandomAccessFile file3 = new RandomAccessFile( "user_on_channel.list", "rw"); int j = 0, k = 0; for(int i = 0; i < channel_counter; i++) { file1.writeBytes(channel_list[i] + "\n" ); } for(int i = 0; i < maximum; i++) { if (user[i] != null && !user[i].equals("server")) { file2.writeBytes(user[i] + "\n" ); j++; } } for(int i = 0; i < maximum; i++) { if (user[i] != null && !user[i].equals("server")) { file3.writeBytes("User " + user[i] + " is on channel " + user_channel[i] + "\n" ); k++; } } for(int i = 0; i < maximum - channel_counter; I++) //Pad file to clear unwanted lines. { file1.writeBytes("\n" ); } for(int i = 0; i < maximum - j; i++) { file2.writeBytes("\n" ); } for(int i = 0; i < maximum - k; i++) { file3.writeBytes("\n" ); } file1.close(); file2.close(); file3.close(); } // End write_info void read_config() throws IOException { String string; Integer integer; RandomAccessFile f = new RandomAccessFile("server.config", "r"); string = f.readLine(); while(string != null) { if (string.startsWith("port")) { string = string.substring(4).trim(); integer = new Integer(string); port = integer.intValue(); } else { if (string.startsWith("server")) { other_server = string.substring(6).trim(); string = f.readLine(); string = string.substring(11).trim(); integer = new Integer(string); other_port = integer.intValue(); } } string = f.readLine(); } f.close(); } public void display() //Update server based on network activity. { String string; string = w.net_line; if (string.startsWith("ADDUSER&")) { adduser(string); } if (string.startsWith("REMOVEUSER&")) { removeuser(string); } if (string.startsWith("TEXT&")) { text(string); } update = 1; } // End display. boolean connect() //Create network to another server connaction. { System.out.println("Info: Connecting server to " + other_server); try { network_client = new Socket(other_server, other_port); } catch (IOException e) { System.out.println("Warning: Unable to connect to host <" + e + ">"); return false; } System.out.println("Info: Connected to server"); try { server_input = new DataInputStream (network_client.getInputStream()); server_output = new PrintStream (network_client.getOutputStream()); } catch (IOException e ) { System.out.println("Warning: Server is NOT ready."); return false; } write_net_output(server_output, "server"); write_net_output(server_output, "server"); return true; } // End connect. void adduser(String input) { boolean new_channel = true; for (int i=0;i < maximum ;i++ ) //Look for empty slot in table { if(user[i] == null) { next_user = i; break; } } user[next_user] = input.substring(8,input.lastIndexOf("&")); user_channel[next_user] = input.substring(input.lastIndexOf("&") + 1).trim(); user_type[next_user] = "R"; for (int k=0; k < channel_counter ; k++) { if (channel_list[k].equals(user_channel[next_user])) { channel_count[k]++; new_channel = false; break; } } if (new_channel) { channel_list[channel_counter] = user_channel[next_user]; channel_count[channel_counter] = 1; channel_counter++; for(int i = 0; i < maximum; i++) { if( user[i] != null && user_type[i].equals("L")) { write_net_output(net_output[i], "ADDCHANNEL&" + user_channel[next_user]); } } } for(int i = 0; i < maximum; i++) { if (user[i] != null && user_channel[next_user].equals(user_channel[i]) && user_type[i].equals("L")) { write_net_output(net_output[i], "ADDUSER&" + user[next_user] ); } } for(int i = 0; i < maximum; i++) { if (user[i] != null && user_type[i].equals("S")) { write_net_output(net_output[i], "ADDUSER&" + user[next_user] + "&" + user_channel[next_user] ); } } } void removeuser(String input) { String person; person = input.substring(11).trim(); for (int i=0;i < maximum ;i++ ) { if ( user[i].equals(person) ) { next_user = i; break; } } for (int k=0; k < channel_counter ; k++) { if (channel_list[k].equals(user_channel[next_user])) { channel_count[k]--; break; } } for(int i = 0; i < maximum; i++) { if (user[i] != null && user_channel[next_user].equals(user_channel[i]) && user_type[i].equals("L")) { write_net_output(net_output[i], "REMOVEUSER&" + user[next_user] ); } } for(int i = 0; i < maximum; i++) { if (user[i] != null && user_type[i].equals("S")) { write_net_output(net_output[i], "REMOVEUSER&" + user[next_user]); } } user[next_user] = null; } void text(String input) //Forward text to correct users. { String message; String person; int index = 0; message = input.substring(5); person = message.substring(0,message.indexOf(" ")); for(int i = 0; i < maximum; i++) { if (user[i] != null && user[i].equals(person)) { index = i; } } System.out.println("Text: Server " + other_server + " says " + input.substring(5)); for(int i = 0; i < maximum; i++) { if (user[i] != null && i != index) { if (user_channel[index].equals(user_channel[i]) || user_type[i].equals("S") ) { if (user_filter[i].indexOf(user[index]) == -1) { if(!user_type[i].equals("R")) { write_net_output(net_output[i], input); } } } } } } } // End server. class reader extends Thread //Thread created to handle user requests. { server s; int index; public reader(server s, int index) //This thread's index and link to server. { this.s = s; this.index = index; } public void run() { setPriority(MIN_PRIORITY); s.user_filter[index] = ""; if (s.user_type[index].equals("S")) { for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && !s.user_type[i].equals("S")) { s.write_net_output(s.net_output[index], "ADDUSER&" + s.user[i] + "&" + s.user_channel[i]); } } } else { s.write_net_output(s.net_output [index], "TEXT&<<<< Adding Startup information... >>>>"); //Add startup info to client. for(int i = 0; i < s.channel_counter; i++) { s.write_net_output(s.net_output[index], "ADDCHANNEL&" + s.channel_list[i]); } for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null) { if(s.user_channel[index].equals(s.user_channel[i]) || s.user_type[i].equals("S") ) { if (s.user_type[i].equals("R")) { s.write_net_output(s.net_output[index], "ADDUSER&" + s.user[i] ); } if (s.user_type[i].equals("S")) { s.write_net_output(s.net_output[i], "ADDUSER&" + s.user[index] + "&" + s.user_channel[index]); } if (s.user_type[i].equals("L")) { s.write_net_output(s.net_output[index], "ADDUSER&" + s.user[i] ); s.write_net_output(s.net_output[i], "ADDUSER&" + s.user[index] ); } } } } s.write_net_output(s.net_output [index], "TEXT&<<<< You are now connected on channel " + s.user_channel[index] + " >>>>"); } s.update = 1; while (true) { String string = s.read_net_input(s.net_input[index]); //Read network. if (string == null) break; System.out.println("Info: " + s.user[index] + " - " + string); if (string.startsWith("TEXT&")) // Handlers for events. { text(string); } if (string.startsWith("GO&")) { gochannel(string); } if (string.startsWith("ADDCHANNEL&")) { addchannel(string); } if (string.startsWith("ADDUSER&")) { adduser(string); } if (string.startsWith("REMOVEUSER&")) { removeuser(string); } if (string.startsWith("FILTER&")) { filter(string); } if (string.startsWith("UNFILTER&")) { unfilter(string); } if (string.startsWith("STATUS&")) { status(string); } } // End while. for(int i = 0; i < s.maximum; i++) //When user quits, remove from other user's lists. { if (s.user[i] != null) { if (s.user_channel[index].equals(s.user_channel[i]) || s.user_type[i].equals("S")) { if(!s.user_type[i].equals("R")) { s.write_net_output(s.net_output[i], "REMOVEUSER&" + s.user[index]); } } } } if (s.connected) { s.write_net_output(s.server_output, "REMOVEUSER&" + s.user[index] ); } for (int i = 0; i < s.channel_counter ; i++) { if (s.channel_list[i].equals(s.user_channel[index])) { s.channel_count[i]--; break; } } s.update = 1; System.out.println("Info: " + s.user[index] + " has disconnected."); s.user[index] = null; } // End run. void text(String input) //Forward text to correct users. { String message; String person; int person_index = 0; System.out.println("Text: " + s.user[index] + " says " + input.substring(5)); if (s.user_type[index].equals("S")) { message = input.substring(5); person = message.substring(0,message.indexOf(" ")); for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && s.user[i].equals(person)) { person_index = i; } } for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && i != person_index && i != index) { if(!s.user_type[i].equals("R")) { if (s.user_channel[person_index].equals( s.user_channel[i]) || s.user_type[i].equals("S") ) { if (s.user_filter[i].indexOf( s.user[person_index]) == -1) { s.write_net_output(s.net_output[i], input); } } } } } } else { for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && i != index) { if(!s.user_type[i].equals("R")) { if (s.user_channel[index].equals( s.user_channel[i]) || s.user_type[i].equals("S") ) { if (s.user_filter[i].indexOf( s.user[index]) == -1) { s.write_net_output(s.net_output[i], "TEXT&" + s.user[index] + " says: " + input.substring(5)); } } } } } } if (s.connected) { s.write_net_output(s.server_output, "TEXT&" + s.user[index] + " says: " + input.substring(5)); } } void gochannel(String input) //Place user on a different channel, removing from other channel { System.out.println("Info: User " + s.user[index] + " is now on channel " + input.substring(3)); for (int k=0; k < s.channel_counter ; k++) { if (s.channel_list[k].equals(s.user_channel[index])) { s.channel_count[k]--; break; } } for (int k=0; k < s.channel_counter ; k++) { if (s.channel_list[k].equals(input.substring(3).trim())) { s.channel_count[k]++; break; } } for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && s.user_channel[index].equals(s.user_channel[i])) { if(s.user_type[i].equals("L")) { s.write_net_output(s.net_output[i], "REMOVEUSER&" + s.user[index] ); } } } s.user_channel[index] = input.substring(3).trim(); s.write_net_output(s.net_output[index], input); s.write_net_output(s.net_output[index], "TEXT&<<<< You are now on channel " + s.user_channel[index] + " >>>>"); for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && s.user_channel[index].equals(s.user_channel[i])) { if(s.user_type[i].equals("L")) { s.write_net_output(s.net_output[i], "ADDUSER&" + s.user[index] ); s.write_net_output(s.net_output[index], "ADDUSER&" + s.user[i] ); } if(s.user_type[i].equals("R")) { s.write_net_output(s.net_output[index], "ADDUSER&" + s.user[i] ); } } else { if(s.user[i] != null && s.user_type[i].equals("S") ) { s.write_net_output(s.net_output[i], "REMOVEUSER&" + s.user[index] ); s.write_net_output(s.net_output[i], "ADDUSER&" + s.user[index] + "&" + s.user_channel[index] ); } } } if (s.connected) { s.write_net_output(s.server_output, "REMOVEUSER&" + s.user[index] ); s.write_net_output(s.server_output, "ADDUSER&" + s.user[index] + "&" + s.user_channel[index] ); } s.update = 1; } void addchannel(String input) //Add a new channel and move user to this channel. { System.out.println("Info: User " + s.user[index] + " is now on new channel " + input.substring(11)); boolean new_channel = true; for (int k=0; k < s.channel_counter ; k++) { if (s.channel_list[k].equals(s.user_channel[index])) { s.channel_count[k]--; break; } } s.user_channel[index] = input.substring(11).trim(); for (int k=0; k < s.channel_counter ; k++) { if (s.channel_list[k].equals(s.user_channel[index])) { s.channel_count[k]++; new_channel = false; break; } } if (new_channel) { s.channel_list[s.channel_counter] = input.substring(11).trim(); s.channel_count[s.channel_counter] = 1; s.channel_counter++; for(int i = 0; i < s.maximum; i++) { if(s.user[i] != null && s.user_type[i].equals("L")) { s.write_net_output(s.net_output[i], "ADDCHANNEL&" + s.user_channel[index]); } } } s.write_net_output(s.net_output[index], "TEXT&<<<< You are now on channel " + s.user_channel[index] + " >>>>"); for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && s.user_channel[index].equals(s.user_channel[i])) { if(s.user_type[i].equals("L")) { s.write_net_output(s.net_output[i], "ADDUSER&" + s.user[index] ); s.write_net_output(s.net_output[index], "ADDUSER&" + s.user[i] ); } } else { if(s.user[i] != null && s.user_type[i].equals("S") ) { s.write_net_output(s.net_output[i], "REMOVEUSER&" + s.user[index] ); s.write_net_output(s.net_output[i], "ADDUSER&" + s.user[index] + "&" + s.user_channel[index] ); } } } if (s.connected) { s.write_net_output(s.server_output, "REMOVEUSER&" + s.user[index] ); s.write_net_output(s.server_output, "ADDUSER&" + s.user[index] + "&" + s.user_channel[index] ); } s.update = 1; } void filter(String input) //Filter (ignore) text from a user. { if (s.user_filter[index].indexOf(input.substring(7)) == -1) { s.user_filter[index] = s.user_filter[index] + input.substring(7).trim() + "|"; } s.write_net_output(s.net_output[index], input); } void unfilter(String input) //Unfilter a user. { String filter_name = input.substring(9).trim() + "|"; if (s.user_filter[index].indexOf(filter_name) != -1) { s.user_filter[index] = s.user_filter[index].substring( 0,s.user_filter[index].indexOf(filter_name)) + s.user_filter[index].substring( s.user_filter[index].indexOf(filter_name) + filter_name.length()); } s.write_net_output(s.net_output[index], input); } void status(String input) //Show server status in client window. { int num_users = 0; int num_servers = 0; for (int i=0;i < s.maximum;i++) { if (s.user[i] != null) { if (s.user_type[i].equals("S")) { num_servers++; } else { num_users++; } } } System.out.println("Info: User " + s.user[index] + " has requested system status"); s.write_net_output(s.net_output[index], "TEXT&<<<< The server has " + num_users + " users connected. >>>>"); s.write_net_output(s.net_output[index], "TEXT&<<<< The server has " + num_servers + " servers connected. >>>>"); s.write_net_output(s.net_output[index], "TEXT&<<<< The server has " + (s.maximum - num_users) + " connections available. >>>>"); s.write_net_output(s.net_output[index], "TEXT&<<<< You are on channel " + s.user_channel[index] + " >>>>"); s.update = 1; } void adduser(String input) { boolean new_channel = true; for (int i=0;i < s.maximum ;i++ ) //Look for empty slot in table { if(s.user[i] == null) { s.next_user = i; break; } } s.user[s.next_user] = input.substring(8,input.lastIndexOf("&")); s.user_channel[s.next_user] = input.substring(input.lastIndexOf("&") + 1).trim(); s.user_type[s.next_user] = "R"; for (int k=0; k < s.channel_counter ; k++) { if (s.channel_list[k].equals(s.user_channel[s.next_user])) { s.channel_count[k]++; new_channel = false; break; } } if (new_channel) { s.channel_list[s.channel_counter] = s.user_channel[s.next_user]; s.channel_count[s.channel_counter] = 1; s.channel_counter++; for(int i = 0; i < s.maximum; i++) { if(s.user[i] != null && s.user_type[i].equals("L")) { s.write_net_output(s.net_output[i], "ADDCHANNEL&" + s.user_channel[s.next_user]); } } } for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && s.user_channel[s.next_user].equals(s.user_channel[i]) && s.user_type[i].equals("L")) { s.write_net_output(s.net_output[i], "ADDUSER&" + s.user[s.next_user] ); } } for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && s.user_type[i].equals("S") && i != index) { s.write_net_output(s.net_output[i], "ADDUSER&" + s.user[s.next_user] + "&" + s.user_channel[s.next_user] ); } } if (s.connected) { s.write_net_output(s.server_output, "ADDUSER&" + s.user[s.next_user] + "&" + s.user_channel[s.next_user] ); } s.update = 1; } void removeuser(String input) { String person; person = input.substring(11).trim(); for (int i=0;i < s.maximum ;i++ ) // { if ( s.user[i].equals(person) ) { s.next_user = i; break; } } for (int k=0; k < s.channel_counter ; k++) { if (s.channel_list[k].equals(s.user_channel[s.next_user])) { s.channel_count[k]--; break; } } for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && i != s.next_user && s.user_channel[s.next_user].equals(s.user_channel[i]) && s.user_type[i].equals("L")) { s.write_net_output(s.net_output[i], "REMOVEUSER&" + s.user[s.next_user] ); } } for(int i = 0; i < s.maximum; i++) { if (s.user[i] != null && i != s.next_user && s.user_type[i].equals("S") && i != index) { s.write_net_output(s.net_output[i], "REMOVEUSER&" + s.user[s.next_user]); } } if (s.connected) { s.write_net_output(s.server_output, "REMOVEUSER&" + s.user[s.next_user]); } s.user[s.next_user] = null; s.update = 1; } } // End reader. class server_writer extends Thread //Thread to read from network, prevents display from blocking while reading from network. { String net_line = ""; server s; public server_writer(server s) { this.s = s; } public void run() { while (true) { String input = s.read_net_input(s.server_input); //Read a line from the network. net_line = input; s.display(); //Call code in display. } } } // End server_writer.