home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / pc / java / in4wjcxu / src / ircd / ircd.java < prev    next >
Encoding:
Java Source  |  1996-08-14  |  15.8 KB  |  676 lines

  1. import java.io.*;
  2. import java.net.*;
  3. import java.util.*;
  4.  
  5. public class IRCd {
  6.     public static void main( String argv[] ) {
  7.         int port = 6667;
  8.         if( argv.length == 1 )
  9.         {
  10.             try {
  11.                 port = new Integer( argv[0] ).intValue();
  12.             } catch( NumberFormatException n ) {
  13.                 port = 6667;
  14.             }
  15.         }
  16.         System.out.println( "Staring IRCd on port: "+port );
  17.         IRCDaemon daemon = new IRCDaemon( port );
  18.     }
  19. }
  20.  
  21. class IRCDaemon {
  22.     ServerSocket server;
  23.     Vector channels;
  24.     Vector users;
  25.     int port = 6667;
  26.  
  27.     public IRCDaemon( int port ) {
  28.         try {
  29.             this.port = port;
  30.             waitForConnections();
  31.         } catch( IOException e ) {
  32.             try {
  33.                 if( server != null ) server.close();
  34.             } catch( Exception ex ) {}
  35.         }
  36.     }
  37.  
  38.     public void waitForConnections() throws IOException {
  39.         channels = new Vector();
  40.         users = new Vector();
  41.  
  42.         server = new ServerSocket( port );
  43.  
  44.         while( true )
  45.         {
  46.             Socket socket = server.accept();
  47.             IRCThread thread = new IRCThread( socket, this );
  48.             thread.start();
  49.         }
  50.     }
  51.  
  52.     synchronized void addUser( User user ) {
  53.         users.addElement( user );
  54.     }
  55.  
  56.     synchronized void removeUser( User user ) {
  57.         users.removeElement( user );
  58.     }
  59.  
  60.     synchronized Vector getUsers() {
  61.         return users;
  62.     }
  63.  
  64.  
  65.     synchronized User getUserByName( String name ) {
  66.         Enumeration e = users.elements();
  67.  
  68.         while( e.hasMoreElements() ) {
  69.             User user = (User)e.nextElement();
  70.             
  71.             if( user.getNickName() != null )
  72.             {
  73.                 if( user.getNickName().equals( name ) )
  74.                     return user;
  75.             }
  76.         }
  77.  
  78.         return null;
  79.     }
  80.  
  81.     synchronized boolean isNickNamePossibleIfYesTakeIt( String name, User ego ) {
  82.         Enumeration e = users.elements();
  83.  
  84.         while( e.hasMoreElements() ) {
  85.             User user = (User)e.nextElement();
  86.         
  87.             if( user.getNickName() != null )
  88.                 if( user.getNickName().equals( name ) ) 
  89.                     return false;
  90.         }
  91.  
  92.         ego.setNickName( name );
  93.         return true;
  94.     }
  95.  
  96.     synchronized Vector getChannels() {
  97.         return channels;
  98.     }
  99.  
  100.     synchronized void partChannelNoMessage( User user, Channel ch ) throws IOException {
  101.         ch.removeUser( user );
  102.         user.setChannel( null );
  103.  
  104.         if( ch.getUsers().isEmpty() )
  105.             channels.removeElement( ch );
  106.     }
  107.  
  108.     synchronized void partChannel( User user, Channel ch ) throws IOException {
  109.         ch.sendToAll( ":"+user.getNickName()+"!email@foo.bar PART "+ch.getChannelName()+"\n" );
  110.  
  111.         partChannelNoMessage( user, ch );
  112.     }
  113.  
  114.     synchronized Channel getChannelByName( String chname ) {
  115.         Enumeration e = channels.elements();
  116.  
  117.         while( e.hasMoreElements() ) {
  118.             Channel ch = (Channel)e.nextElement();
  119.             
  120.             if( ch.getChannelName().equals( chname ) )
  121.             {
  122.                 return ch;
  123.             }
  124.         }
  125.  
  126.         return null;
  127.     }
  128.  
  129.     synchronized Channel joinChannel( User user, String chname ) throws IOException {
  130.         Channel ch = getChannelByName( chname );
  131.  
  132.         if( ch != null )
  133.         {
  134.             joinChannel( user, ch );
  135.             return ch;
  136.         }
  137.  
  138.         // no channel found -> create a new one
  139.  
  140.         ch = new Channel( chname );
  141.         channels.addElement( ch );
  142.  
  143.         joinChannel( user, ch );
  144.         return ch;
  145.     }
  146.  
  147.     synchronized Channel joinChannel( User user, Channel ch ) throws IOException {
  148.         user.setChannel( ch );
  149.         ch.addUser( user );
  150.  
  151.         ch.sendToAll( ":"+user.getNickName()+"!email@foo.bar JOIN :"+ch.getChannelName()+"\n" );
  152.         user.send( ":faui42 353 "+user.getNickName()+" = "+ch.getChannelName()+" : don't care\n" );
  153.         user.send( ":faui42 366 "+user.getNickName()+" "+ch.getChannelName()+" :End of /NAMES.\n" );
  154.         return ch;
  155.     }
  156. }
  157.  
  158. class IRCThread extends Thread {
  159.     DataInputStream input = null;
  160.     DataOutputStream output = null;
  161.     Socket socket = null;
  162.     IRCDaemon ircd = null;
  163.     User ego = null;
  164.  
  165.     public IRCThread( Socket socket, IRCDaemon ircd ) {
  166.         this.socket = socket;
  167.         this.ircd = ircd;
  168.     }
  169.  
  170.     public void run() {
  171.         try {
  172.             input = new DataInputStream( socket.getInputStream() );
  173.             output = new DataOutputStream( socket.getOutputStream() );
  174.  
  175.             waitForInput();
  176.         } catch( IOException e ) {
  177.             Log.log( "Line to '"+ego+"' dropped :"+e.toString() );
  178.  
  179.             try {
  180.                 if( socket != null )
  181.                 {
  182.                     socket.close();
  183.                     input.close();
  184.                     output.close();
  185.                 }
  186.  
  187.                 if( ego != null ) {
  188.                     // oh, this here is diffcult, because the line dropped already
  189.                     // ego.getChannel().sendToAll( ":"+ego.getNickName()+"!email@foo.bar QUIT "+ego.getChannel().getChannelName()+"\n" );
  190.                     ircd.removeUser( ego );
  191.  
  192.                     if( ego.getChannel() != null )
  193.                     {
  194.                         ego.setDataOutputStream( null );
  195.                         ego.setSocket( null );
  196.                         ircd.partChannel( ego, ego.channel );
  197.                         // well to be honest this is a quit, but
  198.                         // it doesn't matter
  199.                     }
  200.                 }
  201.  
  202.             } catch( Exception ex ) {
  203.                 Log.log( "Exception while logging out: "+ex.toString() );
  204.                 ex.printStackTrace();
  205.             }
  206.         }
  207.     }
  208.  
  209.     static final int CMD_ERROR = -1;
  210.     static final int CMD_USER = 0;
  211.     static final int CMD_NICK = 1;
  212.     static final int CMD_PRIVMSG = 2;
  213.     static final int CMD_QUIT = 3;
  214.     static final int CMD_JOIN = 4;
  215.     static final int CMD_PART = 5;
  216.     static final int CMD_LIST = 6;
  217.     static final int CMD_WHO = 7;
  218.     static final int CMD_TOPIC = 8;
  219.     static final int CMD_KICKUSER = 9;
  220.  
  221.     void waitForInput() throws IOException {
  222.         String line;
  223.         boolean running;
  224.         int cmd;
  225.         CallObject command = new CallObject();
  226.         CallObject ret1 = new CallObject();
  227.         CallObject ret2 = new CallObject();
  228.         CallObject ret3 = new CallObject();
  229.         CallObject ret4 = new CallObject();
  230.  
  231.  
  232.         running = true;
  233.         while( running )
  234.         {
  235.             line = input.readLine();
  236.             if( line == null ) throw new IOException();
  237.  
  238.             Log.log( line );
  239.             parseLine( line, command, ret1, ret2, ret3, ret4 );
  240.  
  241.             cmd = ((Integer)command.get()).intValue();
  242.  
  243.             switch( cmd )
  244.             {
  245.                 case CMD_USER:
  246.                     ego = new User( (String)ret4.get(), output, socket );
  247.                     ircd.addUser( ego );
  248.                     break;
  249.                     
  250.                 case CMD_NICK:
  251.                     if( ego == null ) break;
  252.  
  253.                     String newnick = (String)ret1.get();
  254.                     String oldnick = ego.getNickName();
  255.  
  256.                     if( ircd.isNickNamePossibleIfYesTakeIt( newnick, ego ) )
  257.                     {
  258.                         if( oldnick == null ) // logged in now!
  259.                         {
  260.                             // now send the login message!
  261.  
  262.                             output.writeBytes( ":faui42 001 "+newnick+" :Welcome to COMO\n" );
  263.                             output.writeBytes( ":faui42 002 "+newnick+" :Your host is foo.bar\n" );
  264.                             output.writeBytes( ":faui42 003 "+newnick+" :(c) 1996 Jan Kautz\n" );
  265.                             output.writeBytes( ":faui42 004 "+newnick+" :COMO-IRC-Client 1.0\n" );
  266.                             output.writeBytes( ":faui42 251 "+newnick+" :There are "+ircd.getUsers().size()+" users\n" );
  267.                             output.writeBytes( ":faui42 254 "+newnick+" "+ircd.getChannels().size()+" :channels formed\n" );
  268.                             output.writeBytes( ":faui42 375 "+newnick+" :- foo.bar MOTD\n" );
  269.                             output.writeBytes( ":faui42 372 "+newnick+" :- Como-Server\n" );
  270.                             output.writeBytes( ":faui42 372 "+newnick+" :- 96/03/20\n" );
  271.                             output.writeBytes( ":faui42 376 "+newnick+" :End of /MOTD command.\n" );
  272.                         }
  273.  
  274.                         if( ego.getChannel() != null ) {
  275.                             ego.getChannel().sendToAll( ":"+oldnick+"!email@foo.bar NICK :"+newnick+"\n" );
  276.                         } else {
  277.                             if( oldnick != null )
  278.                                 output.writeBytes( ":"+oldnick+"!email@foo.bar NICK :"+newnick+"\n" );
  279.                         }
  280.                     }
  281.                     else
  282.                         output.writeBytes( ":faui42 433 * "+newnick+" :Nickname in use\n" );
  283.                     break;
  284.  
  285.                 case CMD_TOPIC:
  286.                     // TODO::::!!!
  287.                     if( ego == null || ego.getNickName() == null || ego.getChannel() == null ) break;
  288.  
  289.                     String forchannel = (String)ret1.get();
  290.                     String topic = (String)ret2.get();
  291.  
  292.                     Channel topchan = ircd.getChannelByName( forchannel );
  293.                     topchan.setTopic( topic );
  294.                     break;
  295.  
  296.                 case CMD_KICKUSER:
  297.                     if( ego == null || ego.getNickName() == null ) break;
  298.  
  299.                     User kickwhom = ircd.getUserByName( (String)ret1.get() );
  300.  
  301.                     if( kickwhom != null ) {
  302.                         // attention don't kick yourself!!!
  303.  
  304.                         // This causes a Line dropped in the corresponding
  305.                         // readthread :-)
  306.                         kickwhom.getSocket().close();
  307.  
  308.                         /*
  309.                         ircd.removeUser( kickwhom );
  310.  
  311.                         if( kickwhom.channel != null ) ircd.partChannel( kickwhom, kickwhom.channel );
  312.                         */
  313.                     }
  314.                     break;
  315.  
  316.                 case CMD_PRIVMSG:
  317.                     if( ego == null || ego.getNickName() == null ) break;
  318.  
  319.                     String towhat = (String)ret1.get();
  320.  
  321.                     Channel tochan = ircd.getChannelByName( towhat );
  322.                     if( tochan != null ) {
  323.                         tochan.sendToAllExceptOne( ":"+ego.getNickName()+"!email@foo.bar PRIVMSG "+towhat+" :"+(String)ret2.get()+"\n", ego);
  324.                     } 
  325.                     else {
  326.                         // let's look if there's a user named 'towhat'
  327.  
  328.                         User anybody = ircd.getUserByName( towhat );
  329.  
  330.                         if( anybody != null ) {
  331.                             // TODO: check if user is on my channel
  332.  
  333.                             anybody.send( ":"+ego.getNickName()+"!email@foo.bar PRIVMSG "+towhat+" :"+(String)ret2.get()+"\n" );
  334.                         }
  335.                     }
  336.                     break;
  337.  
  338.                 case CMD_JOIN:
  339.                     if( ego == null || ego.getNickName() == null ) break;
  340.                     if( ego.getChannel() != null ) ircd.partChannel( ego, ego.channel );
  341.  
  342.                     Channel jch = ircd.joinChannel( ego, (String)ret1.get() );
  343.                     break;
  344.  
  345.                 case CMD_PART:
  346.                     if( ego == null || ego.getNickName() == null ) break;
  347.                     if( ego.channel != null ) ircd.partChannel( ego, ego.channel );
  348.                     break;
  349.  
  350.                 case CMD_QUIT:
  351.                     running = false;
  352.  
  353.                     if( ego == null ) break;
  354.  
  355.                     ircd.removeUser( ego );
  356.  
  357.                     if( ego.channel != null ) ircd.partChannel( ego, ego.channel );
  358.  
  359.                     running = false;
  360.                     break;
  361.  
  362.                 case CMD_LIST:
  363.                     Enumeration elist = ircd.getChannels().elements();
  364.  
  365.                     output.writeBytes( ":faui42 321 "+ego.getNickName()+" Channel :Users  Name\n" );
  366.                     while( elist.hasMoreElements() ) {
  367.                         Channel channel = (Channel)elist.nextElement();
  368.                         output.writeBytes( ":faui42 322 "+ego.getNickName()+" "+channel.getChannelName()+" "+channel.getUsers().size()+" : "+channel.getTopic()+"\n" );
  369.                     }
  370.                     output.writeBytes( ":faui42 323 "+ego.getNickName()+" :End of /LIST\n" );
  371.                     break;
  372.  
  373.                 case CMD_WHO:
  374.                     String whatchannel = (String)ret1.get();
  375.  
  376.                     if( ego == null ) break;
  377.  
  378.                     // WHO * in a channel means who is in that channel
  379.                     if( ego.getChannel() != null &&
  380.                         (whatchannel == null || whatchannel.equals( "*" ) ) )
  381.                          whatchannel = ego.getChannel().getChannelName();
  382.                     
  383.                     if( whatchannel == null || whatchannel.equals( "*" ) ) {
  384.                             Enumeration e = ircd.getUsers().elements();
  385.  
  386.                             whatchannel = "*";
  387.  
  388.                             while( e.hasMoreElements() ) {
  389.                                 User usr = (User)e.nextElement();
  390.                                 output.writeBytes( ":faui42 352 "+ego.getNickName()+" "+whatchannel+
  391.                                         " loginname "+usr.getSocket().getInetAddress().getHostName()+
  392.                                         " faui42 "+usr.getNickName()+" H :0 "+usr.getRealName()+"\n" );
  393.                             }
  394.                             output.writeBytes( ":faui42 315 "+ego.getNickName()+" "+whatchannel+
  395.                                         " :End of /WHO list.\n" );
  396.                     }
  397.                     else {
  398.                         Channel byname = ircd.getChannelByName( whatchannel );
  399.                         if( byname != null ) {
  400.                             Enumeration e = byname.getUsers().elements();
  401.  
  402.                             while( e.hasMoreElements() ) {
  403.                                 User usr = (User)e.nextElement();
  404.                                 output.writeBytes( ":faui42 352 "+ego.getNickName()+" "+whatchannel+
  405.                                 " loginname "+usr.getSocket().getInetAddress().getHostName()+" faui42 "+
  406.                                 usr.getNickName()+" H :0 "+usr.getRealName()+"\n" );
  407.                             }
  408.                         }
  409.                         output.writeBytes( ":faui42 315 "+ego.getNickName()+" "+whatchannel+
  410.                                         " :End of /WHO list.\n" );
  411.                     }
  412.                     break;
  413.  
  414.                 default:
  415.                     Log.log( "Unknown message :"+line );
  416.                     break;
  417.             }
  418.         }
  419.  
  420.         // now close the socket!
  421.         socket.close();
  422.     }
  423.  
  424.     void parseLine( String line, CallObject command, CallObject ret1, CallObject ret2, CallObject ret3, CallObject ret4 ) {
  425.         StringTokenizer st = new StringTokenizer( line, " \n\r" );
  426.         String str[] = new String[5];
  427.         int i = 0;
  428.         int cmd = CMD_ERROR;
  429.  
  430.         command.set( new Integer( cmd ) );
  431.  
  432.         while( st.hasMoreTokens() ) {
  433.             str[i] = st.nextToken();
  434.             i++;
  435.             if( i == 5 ) break;
  436.         }
  437.  
  438.         if( i == 0 ) return;
  439.  
  440.         if( str[0].equalsIgnoreCase( "NICK" ) ) cmd = CMD_NICK;
  441.         else if( str[0].equalsIgnoreCase( "JOIN" ) ) cmd = CMD_JOIN;
  442.         else if( str[0].equalsIgnoreCase( "USER" ) ) cmd = CMD_USER;
  443.         else if( str[0].equalsIgnoreCase( "PART" ) ) cmd = CMD_PART;
  444.         else if( str[0].equalsIgnoreCase( "QUIT" ) ) cmd = CMD_QUIT;
  445.         else if( str[0].equalsIgnoreCase( "WHO" ) ) cmd = CMD_WHO;
  446.         else if( str[0].equalsIgnoreCase( "LIST" ) ) cmd = CMD_LIST;
  447.         else if( str[0].equalsIgnoreCase( "PRIVMSG" ) ) cmd = CMD_PRIVMSG;
  448.         else if( str[0].equalsIgnoreCase( "TOPIC" ) ) cmd = CMD_TOPIC;
  449.         else if( str[0].equalsIgnoreCase( "KICKUSER" ) ) cmd = CMD_KICKUSER;
  450.         else return;
  451.  
  452.         switch( cmd ) {
  453.             case CMD_USER:
  454.                 if( i < 5 ) return;
  455.  
  456.                 ret1.set( str[1] );
  457.                 ret2.set( str[2] );
  458.                 ret3.set( str[3] );
  459.                 ret4.set( str[4] );
  460.  
  461.                 int ix = line.indexOf( ':' );
  462.                 if( ix < 0 ) break;
  463.  
  464.                 ret4.set( line.substring( ix+1 ) );
  465.                 break;
  466.  
  467.             case CMD_NICK:
  468.                 if( i != 2 ) return;
  469.  
  470.                 ret1.set( str[1] );
  471.                 break;
  472.  
  473.             case CMD_PRIVMSG:
  474.                 ret1.set( str[1] );
  475.                 int index = line.indexOf( ':' );
  476.                 if( index < 0 ) return;
  477.                 ret2.set( line.substring( index+1 ) );
  478.                 break;
  479.  
  480.             case CMD_TOPIC:
  481.                 ret1.set( str[1] );
  482.                 int idx = line.indexOf( ':' );
  483.                 if( idx < 0 ) return;
  484.                 ret2.set( line.substring( idx+1 ) );
  485.                 break;
  486.  
  487.             case CMD_QUIT:
  488.                 if( i != 1 ) return;
  489.  
  490.                 ret1.set( str[1] );
  491.                 break;
  492.  
  493.             case CMD_JOIN:
  494.                 if( i != 2 ) return;
  495.  
  496.                 ret1.set( str[1] );
  497.                 break;
  498.                 
  499.             case CMD_PART:
  500.                 if( i != 2 ) return;
  501.  
  502.                 ret1.set( str[1] );
  503.                 break;
  504.  
  505.             case CMD_KICKUSER:
  506.                 if( i != 2 ) return;
  507.  
  508.                 ret1.set( str[1] );
  509.                 break;
  510.  
  511.             case CMD_WHO:
  512.                 if( i != 1 && i != 2 ) return;
  513.  
  514.                 ret1.set( str[1] );
  515.                 break;
  516.  
  517.             case CMD_LIST:
  518.                 if( i != 1 && i != 2 ) return;
  519.  
  520.                 ret1.set( str[1] );
  521.                 break;
  522.         }
  523.  
  524.         command.set( new Integer( cmd ) );
  525.         return;
  526.     }
  527. }
  528.  
  529. class User {
  530.     String realname = null;
  531.     String nickname = null;
  532.     Channel channel = null;
  533.     DataOutputStream output = null;
  534.     Socket socket = null;
  535.  
  536.     public User( String real, DataOutputStream output, Socket socket ) {
  537.         realname = real;
  538.         this.output = output;
  539.         this.socket = socket;
  540.     }
  541.  
  542.     public User( String real, String nick, DataOutputStream output ) {
  543.         realname = real;
  544.         nickname = nick;
  545.         this.output = output;
  546.         this.socket = socket;
  547.     }
  548.  
  549.     public void setNickName( String name ) {
  550.         nickname = name;
  551.     }
  552.     
  553.     public String getNickName() {
  554.         return nickname;
  555.     }
  556.     
  557.     
  558.     public String getRealName() {
  559.         return realname;
  560.     }
  561.     
  562.     public void setChannel( Channel ch ) {
  563.         channel = ch;
  564.     }
  565.  
  566.     public Channel getChannel() {
  567.         return channel;
  568.     }
  569.  
  570.     public void setDataOutputStream( DataOutputStream output ) {
  571.         this.output = output;
  572.     }
  573.  
  574.     public DataOutputStream getDataOutputStream() {
  575.         return output;
  576.     }
  577.  
  578.     public void setSocket( Socket socket ) {
  579.         this.socket = socket;
  580.     }
  581.  
  582.     public Socket getSocket() {
  583.         return socket;
  584.     }
  585.  
  586.     synchronized public void send( String str ) throws IOException {
  587.         if( output != null )
  588.         {
  589.             output.writeBytes( str );
  590.             output.flush();
  591.         }
  592.     }
  593.  
  594.     public String toString() {
  595.         return "Nick: "+nickname+" Real: "+realname;
  596.     }
  597. }
  598.  
  599. class Channel {
  600.     String name;
  601.     Vector users;
  602.     String topic = "none";
  603.  
  604.     public Channel( String name ) {
  605.         this.name = name;
  606.         users = new Vector();
  607.     }
  608.  
  609.     public Vector getUsers() {
  610.         return users;
  611.     }
  612.  
  613.     public void addUser( User user ) {
  614.         users.addElement( user );
  615.     }
  616.  
  617.     public void removeUser( User user ) {
  618.         users.removeElement( user );
  619.     }
  620.  
  621.     public String getTopic() {
  622.         return topic;
  623.     }
  624.  
  625.     public void setTopic( String topic ) {
  626.         this.topic = topic;
  627.     }
  628.  
  629.     public String getChannelName() {
  630.         return name;
  631.     }
  632.  
  633.     public void sendToAll( String str ) {
  634.         sendToAllExceptOne( str, null );
  635.     }
  636.  
  637.     public void sendToAllExceptOne( String str, User except ) {
  638.         Enumeration e = users.elements();
  639.  
  640.         while( e.hasMoreElements() ) {
  641.             User user = (User)e.nextElement();
  642.  
  643.             if( user != except )
  644.             {
  645.                 try {
  646.                     user.send( str );
  647.                 } catch( IOException exception ) {
  648.                     if( user.getSocket() != null ) 
  649.                     {
  650.                         // this will cause a line-drop in the read-thread
  651.                         // of that user!
  652.                         try {
  653.                             user.getSocket().close();
  654.                         } catch( Exception ekzeption ) {}
  655.                     }
  656.                 }
  657.             }
  658.         }
  659.     }
  660.  
  661.     public String toString() {
  662.         return "Channel: "+name + " Users: "+users;
  663.     }
  664. }
  665.  
  666. class Log {
  667.     public Log() {}
  668.  
  669.     static void log( int val, String out ) {
  670.         log( val+" "+out );
  671.     }
  672.     static void log( String out ) {
  673.         System.out.println( out );
  674.     }
  675. }
  676.