home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / pc / java / in4wjcxu / src / como / irc / comobjirc.java next >
Encoding:
Java Source  |  1996-08-14  |  21.3 KB  |  909 lines

  1. /*
  2. * @(#)ComObjIRC.java    1.0 96/03/03 Ulrich Gall & Jan Kautz
  3. *
  4. * Copyright (c) 1996 Ulrich Gall & Jan Kautz 
  5. * uhgall@cip.informatik.uni-erlangen.de
  6. * jnkautz@cip.informatik.uni-erlangen.de
  7. * Hofmannstr. 48, D-91052 Erlangen, Germany, Fax: +49-9131-201358
  8. *
  9. */
  10.  
  11. package como.irc;
  12.  
  13. import java.io.*;
  14. import java.util.*;
  15. import java.awt.*;
  16. import java.applet.*;
  17. import java.net.*;
  18.  
  19. import como.awt.*;
  20. import como.commlet.*;
  21. import como.util.*;
  22. import como.sys.*;
  23.  
  24. public class ComObjIRC implements ComObj {
  25.     private Hashtable involvedusers;
  26.     private Hashtable allowedusers;
  27.     private Hashtable usersleft;
  28.     private Vector cache;
  29.     private ServerIRC server;
  30.     private Commlet commlet = null;
  31.     private String commletname = "not_set.class";
  32.     private String topic = "No topic set!";
  33.     private Thread readthread;
  34.     private CubbyHole cubbyhole;
  35.     private IrcChan chan;
  36.     private IrcSocket socket;
  37.     private int myID;
  38.     private int masterID;
  39.     private boolean masterflag = false;
  40.     protected boolean do_not_get_any_more_messages = false;
  41.     private boolean i_am_not_allowed = false;
  42.  
  43.     /**
  44.      * Start ComObjIRC. Establish Connection to given Channel.
  45.      * Set up ReadThread for it.
  46.      */
  47.     ComObjIRC( ServerIRC s, IrcChan c, User ego, boolean iammaster ) throws IOException {
  48.         chan = c;
  49.         server = s;
  50.         masterID = -1;
  51.         masterflag = iammaster;
  52.  
  53.         involvedusers = new Hashtable();
  54.         allowedusers  = new Hashtable();
  55.         usersleft     = new Hashtable();
  56.         cache         = new Vector();
  57.  
  58.         // create cubbyhole for answer messages
  59.         cubbyhole = new CubbyHole();
  60.  
  61.         // open connection to ircd on the server
  62.         // and join the channel chan.ircName()
  63.         try {
  64.             socket = new IrcSocket( server, chan );
  65.         } catch( IOException e ) {
  66.             // this means to me: leave
  67.             throw new IOException();
  68.         }
  69.  
  70.         topic = chan.topic;
  71.  
  72.         /* this here is really ugly, but what shall I do else? */
  73.         ego.put( User.NICK, socket.nick );
  74.         myID = calcHashCode( ego );
  75.         ego.put( User.ID, new Integer(myID) );
  76.  
  77.         involvedusers.put( new Integer(myID), ego );
  78.  
  79.         if( iammaster ) masterID = myID;
  80.  
  81.         // start my ReadThread, which reads from the given socket
  82.         readthread = new ReadThreadIRC( this, socket );
  83.         readthread.start();
  84.     }
  85.  
  86.  
  87.     /**
  88.      * set the Name of the commlet
  89.      */
  90.     synchronized public void setCommletName( String name ) {
  91.         commletname = name;
  92.     }
  93.  
  94.     /**
  95.      * Tell the ComObj about his commlet. This is necessary this way
  96.      * because the ComObj is instantiated before the commlet
  97.      */
  98.     synchronized public void setCommlet( Commlet c ) {
  99.         commlet = c;
  100.     }
  101.  
  102.     /**
  103.      * return the Commlet
  104.      */
  105.     public Commlet getCommlet() {
  106.         return commlet;
  107.     }
  108.  
  109.     /**
  110.      * returns the master ID.
  111.      * -1 means it is still unknown.
  112.      */
  113.     synchronized public int getMasterID() {
  114.         return masterID;
  115.     }
  116.  
  117.     /**
  118.      * returns my ID.
  119.      * -1 means it is still unknown (I didn't login myself to this commlet/comobj).
  120.      */
  121.     public int getMyID() {
  122.         return myID;
  123.     }
  124.  
  125.     /**
  126.      * return true if i am the master
  127.      */
  128.     synchronized public boolean iAmMaster() {
  129.         return masterflag;
  130.     }
  131.  
  132.     /**
  133.      * set a new topic (in the irc-channel) and tell others
  134.      * about it.
  135.      */
  136.     synchronized public void setNewTopic( String topic ) {
  137.         this.topic = topic;
  138.         socket.send( "TOPIC #"+chan.ircName()+" :"+topic, true );
  139.         sendToAll( new Msg( Msg.NEW_TOPIC, topic ) );
  140.     }
  141.  
  142.     /**
  143.      * It will set the which-User-attributes to the newuser-attributes,
  144.      * paying attention to those attributes, who may not be altered.
  145.      */
  146.     synchronized private void setUser( User which, User newuser ) {
  147.         Enumeration e = newuser.keys();
  148.         while( e.hasMoreElements() )
  149.         {
  150.             Object key;
  151.             Object elem;
  152.             Integer i;
  153.  
  154.             key = e.nextElement();
  155.             elem = newuser.get( key );
  156.             if( key instanceof Integer )
  157.             {
  158.                 i = (Integer)key;
  159.  
  160.                 // things that I don't want to be overwritten
  161.                 if( i.equals( User.NICK ) || i.equals( User.COMOUSER ) ||
  162.                     i.equals( User.SOCKET ) || i.equals( User.ID ) ) continue;
  163.             }
  164.  
  165.             which.put( key, elem );
  166.         }
  167.     }
  168.  
  169.     /**
  170.      * Change some attributes of the local user.
  171.      * Some can't be changed (NICK, SOCKET, ID,...).
  172.      */
  173.     synchronized public void setLocalUser( User user ) {
  174.         setUser( getUser( getMyID() ), user );
  175.         sendToAll( new Msg( Msg.NEW_USER_INFO, getUser( getMyID() ) ) );
  176.     }
  177.  
  178.     /**
  179.      * return the User-information of the user with id.
  180.      */
  181.     synchronized public User getUser( int id ) {
  182.         // TODO: get more information about him!!!
  183.         // ask himself, perhaps
  184.         // no dont ask. he will tell us automatically
  185.  
  186.         User user = (User)involvedusers.get( new Integer( id ) );
  187.  
  188.         // now let's try to find him in userleft
  189.         if( user == null ) {
  190.             user = (User)usersleft.get( new Integer( id ) );
  191.         }
  192.  
  193.         return user;
  194.     }
  195.  
  196.     /**
  197.      * return the Username of the user with id.
  198.      */
  199.     synchronized public String getUserName( int id ) {
  200.         if( id == -1 ) {
  201.             return "NoName";
  202.         } else {
  203.             User user = getUser( id );
  204.             return (String)user.get( User.NAME );
  205.         }
  206.     }
  207.  
  208.     /**
  209.      * return a Vector of all involved Users
  210.      */
  211.     synchronized public Vector getUsers() {
  212.         Vector v = new Vector();
  213.         Enumeration e = involvedusers.elements();
  214.         User me;
  215.  
  216.         // I want to have me as the first in the list!
  217.  
  218.         me = getUser( getMyID() );
  219.         if( me == null ) return v;
  220.  
  221.         v.addElement( me.clone() );
  222.  
  223.         while( e.hasMoreElements() ) {
  224.             User u = (User)e.nextElement();
  225.  
  226.             if( me == u ) continue;    // I am already in there
  227.  
  228.             u = (User)u.clone();        // don't show him the real and only
  229.             v.addElement( u );
  230.         }
  231.         
  232.         return v;
  233.     }
  234.  
  235.     /**
  236.      * Send Msg to msg.to
  237.      */
  238.     synchronized public void sendTo( Msg msg ) {
  239.         User user = getUser( msg.to );
  240.         msg.from = getMyID();
  241.  
  242. /* Well this is a bad thing here:
  243.    If I call it like this, the handleMsg()
  244.     may call again sendTo() which may call again
  245.     handleMsg()... Then the handleMsg() can never
  246.     be synchronized!!! 
  247.  
  248.         if( msg.to == getMyID() )
  249.             commlet.handleMsg( msg );
  250.         else
  251. */
  252.  
  253.         {
  254.             if( user == null )
  255.                 Debug.msg( 45, "ComObjIRC.sendTo(): unknown user "+msg.to );
  256.             else
  257.                 sendTo( user, msg );
  258.         }
  259.     }
  260.  
  261.     /**
  262.      * Send Message to User user
  263.      */
  264.     synchronized private void sendTo( User user, Msg msg ) {
  265.         msg.from = getMyID();
  266.         socket.writePrivMsg( user, msg );
  267.     }
  268.  
  269.     /**
  270.      * Send Msg to all users of this session except myself.
  271.      */
  272.     synchronized public void sendToOthers( Msg msg ) {
  273.         msg.from = getMyID();
  274.         msg.to = 0;
  275.         socket.writeMsg( msg );
  276.     }
  277.  
  278.     /**
  279.      * Send Msg to all users of this session including myself.
  280.      */
  281.     synchronized public void sendToAll( Msg msg ) {
  282.         // to the others
  283.         sendToOthers( msg );
  284.  
  285.  
  286.         // to myself
  287.         msg.to = msg.from = getMyID();
  288.         sendTo( msg );
  289.  
  290.         // If you wonder why we are sending 
  291.         // even my own things to the IRC-Server:
  292.         // We think it is necessary, that Messages
  293.         // that are sent to everyone by e.g. two users
  294.         // should arrive in the same order everywhere!
  295.         // This can only be guaranteed, if we send
  296.         // it back to the IRC-Server.
  297.         // If you only have commlets, that don't insist
  298.         // on this, you could use:
  299.         // commlet.handleMsg( msg );
  300.         // But then you have to make a Thread for it, because
  301.         // it could block everything otherwise!!!
  302.     }
  303.  
  304.     /**
  305.      * Send Msg to group of users of this session.
  306.      */
  307.     synchronized public void sendToGroup( int to[], Msg msg ) {
  308.         int len = to.length;
  309.  
  310.         for( int i = 0; i < len; i++ ) {
  311.             msg.to = to[i];
  312.             sendTo( msg );
  313.         }
  314.     }
  315.  
  316.     /**
  317.      * ask msg.to msg. Then wait for a return-msg. The type
  318.      * of the return msg must be -msg.type of the question!!
  319.      */
  320.     public synchronized Msg ask( Msg msg ) {
  321.         sendTo( msg );
  322.  
  323.         // answer must be: -Msg.type !!!
  324.         // else I will wait until doom's day!
  325.  
  326.         return (Msg)cubbyhole.get();
  327.     }    
  328.  
  329.  
  330.     /**
  331.      * kickUser kicks a User out of this channel!
  332.      * It send him a KICK_USER message!
  333.      */
  334.     synchronized public void kickUser( int id, String reason ) {
  335.         sendTo( new Msg( Msg.KICK_USER, getMyID(), id, reason ) );
  336.     }
  337.  
  338.     /**
  339.      * This method is invoked when a user left. It is called in every
  340.      * ComObj belonging to this communication. In every comobj it WILL
  341.      * choose the same User as master!
  342.      * This is done by choosing the user with the lowest ID.
  343.      */
  344.     synchronized private void chooseNewMaster() {
  345.         Enumeration e = involvedusers.elements();
  346.         int lowest_id = -1; // ids in ComObjIRC are always positive
  347.  
  348.         while( e.hasMoreElements() ) {
  349.             User u = (User)e.nextElement();
  350.             int uid = ((Integer)u.get( User.ID )).intValue();
  351.  
  352.             if( lowest_id == -1 ) {
  353.                 lowest_id = uid;
  354.             }
  355.             else {
  356.                 if( uid < lowest_id )
  357.                     lowest_id = uid;
  358.             }
  359.         }
  360.  
  361.         masterID = lowest_id;    // now he is the new master
  362.         if( masterID == getMyID() ) {
  363.             masterflag = true;
  364.         }
  365.     }
  366.  
  367.  
  368.     /**
  369.      * Here we can handle msg for the ComObj (or messages
  370.      * important for the ComObj)
  371.      */
  372.     synchronized public Msg preHandleMsg( Msg msg ) {
  373.         User user = null;
  374.  
  375.         switch( msg.type ) {
  376.             Integer userid;
  377.             int id;
  378.  
  379.             case Msg.INVITATION:
  380.                 // This message is for the server!
  381.                 server.handleMsg( msg );
  382.                 return null;
  383.  
  384.             case Msg.KICK_USER:
  385.                 String reason = (String)msg.arg;
  386.                 Panel msgpanel = new Panel();
  387.                 if( reason == null ) reason = " ";
  388.                 msgpanel.setLayout( new VertLayout( VertLayout.STRETCH ) );
  389.                 msgpanel.add( new Label( "Sorry, you've been kicked out of the Channel" ) );
  390.                 msgpanel.add( new Label( "by "+getUserName( msg.from )+"!" ) );
  391.                 msgpanel.add( new Label( "Reason: "+(String)msg.arg ) );
  392.  
  393.                 new SmartFrame( msgpanel, "OK" );
  394.                 destroy();
  395.  
  396.                 // tell thread to stop now (will also be stopped by
  397.                 // comobj.logout(), called in getCommlet().stop()
  398.                 return msg;
  399.  
  400.             case Msg.NOT_ALLOWED:
  401.                 new SmartFrame( "Sorry, you are not allowed to join! Please quit your Commlet!" );
  402.  
  403.                 i_am_not_allowed = true;
  404.                 sendPartMessage();
  405.  
  406.                 // I wanted to quit it automatically, but this 
  407.                 // does not work, because the message NOT_ALLOWED
  408.                 // comes before everything is initialized!
  409.                 // So don't do this. -> Let the user quit manually.
  410.                 // destroy();
  411.  
  412.                 // tell thread to stop now! This is at least one
  413.                 // thing i can do, get no more incoming messages!
  414.                 return msg;
  415.  
  416.             case Msg.LINE_DROPPED:
  417.                 // Tell the commlet, that it must quit now!
  418.  
  419.                 new SmartFrame( "Sorry, line to server dropped!" );
  420.                 destroy();
  421.  
  422.                 // Sorry this is a special case :-(
  423.                 // Thread must know, that he must stop
  424.                 return msg;
  425.  
  426.             case Msg.USER_LEFT:
  427.                 String nick = (String)msg.arg;
  428.                 Enumeration e = involvedusers.keys();
  429.                 Integer testid;
  430.  
  431.                 userid = new Integer( -1 );
  432.  
  433.                 // search the nick in the involveduser-List 
  434.                 // and get his ID
  435.                 while( e.hasMoreElements() )
  436.                 {
  437.                     testid = (Integer)e.nextElement();
  438.                     user = (User)involvedusers.get( testid );
  439.                     if( ((String)user.get( User.NICK )).compareTo( nick ) == 0 )
  440.                     {
  441.                         userid = testid;
  442.                         break;
  443.                     }
  444.                 }
  445.                 if( userid.intValue() == -1 )
  446.                 {
  447.                     Debug.msg( 43, "ComObjIRC.preHandleMsg(USER_LEFT): Unknown User "+nick+" left" );
  448.                     return null;
  449.                 }
  450.  
  451.                 // allowedusers.remove() should not be necessary here
  452.                 // user should have been removed already (in addUser())
  453.                 allowedusers.remove( userid );
  454.                 involvedusers.remove( userid );
  455.  
  456.                 usersleft.put( userid, user );
  457.  
  458.                 // was the user who the master? yes??
  459.                 // then choose a new one. just take the user
  460.                 // with the lowest ID. then tell the commlet
  461.                 chooseNewMaster();
  462.                 commlet.handleMsg( new Msg( Msg.NEW_MASTER, getMasterID(), getMyID(), new Integer(getMasterID()) ) );
  463.  
  464.                 // now tell the commlet, that a user left.
  465.                 // make sure, that if he/she calls getUsers() the leaving user
  466.                 // is not in that list! But if he/she calls getUser(userid)
  467.                 // he/she still gets information about him/her!
  468.                 commlet.handleMsg( new Msg( Msg.USER_LEFT, userid.intValue(), getMyID(), userid ) );
  469.  
  470.                 // TODO: remove it here ?
  471.                 // or always remember 10 users, or ...
  472.                 // But it seems ok like this....
  473.                 // perhaps sometimes I should change this!
  474.                 usersleft.remove( userid );
  475.                 return null;
  476.  
  477.             case Msg.ADD_USER:
  478.                 User who = (User)msg.arg;
  479.  
  480.                 if( (id = loginUser( who )) > 0 )
  481.                     addUser( id );
  482.                 else {
  483.                     // well if i am the master tell him to leave again!
  484.  
  485.                     if( iAmMaster() )
  486.                         sendTo( who, new Msg( Msg.NOT_ALLOWED ) );
  487.                 }
  488.                 return null;
  489.  
  490.             case Msg.GET_USER_INFO:
  491.                 Msg retmsg;
  492.  
  493.                 // oh, I'm still not here
  494.                 if( getMyID() == -1 ) {
  495.                     cacheMsg( msg );
  496.                     return null;
  497.                 }
  498.  
  499.                 user = getUser( getMyID() );
  500.  
  501.                 // construct answer message for that query
  502.                 retmsg = new Msg( Msg.NEW_USER_INFO, 0, msg.from, user );
  503.                 sendTo( retmsg );
  504.                 return null;
  505.  
  506.             case Msg.NEW_USER_INFO:
  507.                 User newuser = (User)msg.arg, olduser;
  508.                 userid = new Integer(msg.from);
  509.  
  510.                 olduser = (User)involvedusers.get( userid );
  511.                 if( olduser != null ) {
  512.                     // Attention here:
  513.                     // give him the old user info
  514.                     // he has to get the new via getUser()
  515.                     msg.arg = olduser.clone();
  516.  
  517.                     // set the olduser correctly
  518.                     setUser( olduser, newuser );
  519.  
  520.                     return msg;
  521.                 } else {
  522.                     cacheMsg( msg );
  523.                     return null;
  524.                 }
  525.  
  526.             case Msg.NEW_MASTER:
  527.                 Integer i = (Integer)msg.arg;
  528.  
  529.                 if( (User)involvedusers.get( i ) == null ) {
  530.                     cacheMsg( msg );
  531.                     return null;
  532.                 }
  533.  
  534.                 if( masterflag == true )
  535.                     Debug.msg( 55, "ComObjIRC().preHandleMsg().NEW_MASTER: impossible message" );
  536.  
  537.                 masterID = i.intValue();
  538.  
  539.                 if( masterID == getMyID() ) masterflag = true;
  540.  
  541.                 // well nice to know this, but the commlet also
  542.                 // wants to know it!
  543.                 return msg;
  544.  
  545.             case Msg.NEW_TOPIC:
  546.  
  547.                 if( involvedusers.get( new Integer( msg.from ) ) == null ) {
  548.                     cacheMsg( msg );
  549.                     return null;
  550.                 }
  551.                 else {
  552.                     topic = (String)msg.arg;
  553.                     return msg;
  554.                 }
  555.  
  556.             case Msg.NO_DATA:
  557.                 return null;
  558.  
  559.             default:
  560.                 if( involvedusers.get( new Integer( msg.from ) ) == null )
  561.                 {
  562.                     cacheMsg( msg );
  563.                     return null;
  564.                 }
  565.  
  566.                 if( msg.isAnswer() )  // It is a answer for something!!
  567.                 {
  568.                     cubbyhole.put( msg );
  569.                     return null;
  570.                 }
  571.                 
  572.                 return msg;
  573.         }
  574.     }
  575.  
  576.     /**
  577.      * Here I can cache messages. This is usefull, if I get a message
  578.      * from a user that I don't know yet.
  579.      */
  580.     synchronized private void cacheMsg( Msg msg ) {
  581.         long mil = System.currentTimeMillis();    // current time in milliseconds since 1970
  582.         Long millis = new Long( mil ); 
  583.  
  584.         Vector elem = new Vector();
  585.         elem.addElement( millis );
  586.         elem.addElement( msg );
  587.  
  588.         cache.addElement( elem );
  589.     }
  590.  
  591.     /**
  592.      * Search the cache if I have messages from the specified user,
  593.      * if true, then deliver it (i.e. commlet.handleMsg())
  594.      * else, keep the messages.
  595.      * This is called, when a user is added.
  596.      */
  597.     synchronized private void handleCachedMsg( int id ) {
  598.         Enumeration e = cache.elements();
  599.  
  600.         while( e.hasMoreElements() ) {
  601.             Vector elem = (Vector)e.nextElement();
  602.             Msg msg = (Msg)elem.elementAt( 1 );
  603.  
  604.             if( msg.from == id ) {
  605.                 // aaah, it's from her or him
  606.  
  607.                 cache.removeElement( elem );
  608.  
  609.                 // I have to ask for a new enumeration!!!
  610.                 // Removing an element causes the
  611.                 // Enumeration to stop an element earlier
  612.                 // than it should (look at java/util/Vector.java)
  613.                 e = cache.elements();
  614.  
  615.                 if( preHandleMsg( msg ) != null )
  616.                 {
  617.                     // ok it's a message for the commlet
  618.  
  619.                     // well i have to set the 'to' here, because it could
  620.                     // be zero (equals to all)
  621.                     msg.to = getMyID();
  622.                     commlet.handleMsg( msg );
  623.                 }
  624.             }
  625.         }
  626.     }
  627.  
  628.     /**
  629.      * Search the cache if I have messages older than 60 seconds;
  630.      * if true delete them.
  631.      */
  632.     synchronized protected void removeOldCachedMsg() {
  633.         Enumeration e = cache.elements();
  634.  
  635.         while( e.hasMoreElements() ) {
  636.             Vector elem = (Vector)e.nextElement();
  637.             Long millis = (Long)elem.elementAt(0);
  638.  
  639.             if( millis.longValue() < (System.currentTimeMillis() - (60 * 10 * 1000)) ) {
  640.                 // aaah, it's too old
  641.  
  642.                 Msg msg = (Msg)elem.elementAt( 1 );
  643.  
  644.                 cache.removeElement( elem );
  645.             }
  646.         }
  647.     }
  648.  
  649.     /**
  650.      * Asks commlet if the User is admitted to enter.
  651.      */
  652.     synchronized public int loginUser( User user ) {
  653.  
  654.         // let's see if I was permitted to join the commlet
  655.         // if not, don't add any users.
  656.         if( i_am_not_allowed ) return -1;
  657.  
  658.         if( commlet.isUserAdmitted( user ) ) {
  659.             int id = calcHashCode( user );
  660.  
  661.             allowedusers.put( new Integer(id), user );
  662.             return id;
  663.         }
  664.         else
  665.             return -1;
  666.     }
  667.  
  668.     /**
  669.      * Adds a user who has already logged in.
  670.      * It tells this user, who i am and if i am the master
  671.      */
  672.     synchronized public void addUser( int userid ) {
  673.         User user;
  674.         
  675.         if( (user = (User)allowedusers.get( new Integer(userid) )) != null )
  676.         {
  677.             user.put( User.ID, new Integer(userid) );
  678.             involvedusers.put( new Integer(userid), user );
  679.             allowedusers.remove( new Integer(userid) );
  680.             commlet.handleMsg( new Msg( Msg.ADD_USER, new Integer( userid ) ) );
  681.  
  682.             // now handle the cached messages
  683.             handleCachedMsg( userid );
  684.  
  685.             // now tell her/him who i am.
  686.             sendTo( new Msg( Msg.NEW_USER_INFO, getMyID(), userid, getUser( getMyID() ) ) );
  687.  
  688.             // if i am master tell him/her
  689.             // and tell her the topic of this channel
  690.             if( iAmMaster() )
  691.             {
  692.                 sendTo( new Msg( Msg.NEW_MASTER, getMyID(), userid, new Integer(getMyID()) ) );
  693.                 sendTo( new Msg( Msg.NEW_TOPIC, getMyID(), userid, topic ) );
  694.             }
  695.         }
  696.         else
  697.             Debug.msg( 98, "Not allowed user called ComObj.addUser()" );
  698.         return;
  699.     }
  700.  
  701.     /**
  702.      * Add myself to this Commlet.
  703.      * If i am master then set the topic
  704.      */
  705.     synchronized public void addMe( User ego ) {
  706.         commlet.handleMsg( new Msg( Msg.ADD_USER, new Integer(myID) ) );
  707.  
  708.         if( iAmMaster() )
  709.         {
  710.             setNewTopic( topic );
  711.         }
  712.     }
  713.  
  714.     /**
  715.      * Calc a HashCode out of a user. Avoids same hashcode for
  716.      * different nick names.
  717.      */
  718.     synchronized private int calcHashCode( User user ) {
  719.         String nick = (String)user.get( User.NICK );
  720.         int id = nick.hashCode();
  721.  
  722.         if( id < 0 ) id = -id;
  723.             
  724.         // as long as the id is already used !
  725.         while( involvedusers.containsKey( new Integer(id) ) || 
  726.                     allowedusers.containsKey( new Integer(id) ) )
  727.             id++;    
  728.         
  729.         return id;    
  730.     }
  731.  
  732.     synchronized private void sendPartMessage() {
  733.         try {
  734.             socket.send( "PART #"+chan.ircName(), true );
  735.         } catch( Exception e ) {
  736.             Debug.msg( 75, "ComObjIRC.logout(): Couldn't send PART-Message anymore" );
  737.         }
  738.     }
  739.  
  740.     /**
  741.      * This is called in order to quit the ComObj.
  742.      * It also tells the server, that i quitted.
  743.      */
  744.     synchronized public void logout() {
  745.         do_not_get_any_more_messages = true;
  746.  
  747.         sendPartMessage();
  748.  
  749.         try {
  750.             socket.close();
  751.         } catch( Exception e ) {
  752.             // noone cares anymore here!
  753.             Debug.msg( 75, "ComObjIRC.logout(): Couldn't close socket." );
  754.         }
  755.  
  756.         // I have to stop the read-thread after I quitted the IRC.
  757.         // The other way I had problems to close the socket.
  758.         // Don't ask me why!
  759.  
  760.         if( readthread != null && Thread.currentThread() != readthread )
  761.         {
  762.             // stop the readthread. then it is not possible anymore
  763.             // to get message!
  764.             readthread.stop();
  765.             readthread = null;
  766.  
  767.         }
  768.  
  769.         server.loggedout( this, chan );
  770.     }
  771.  
  772.     /**
  773.      * Destroy everything in here. I.e. also quit the commlet.
  774.      * It will also call logout().
  775.      */
  776.     public void destroy() {
  777.         commlet.stop();
  778.         // stops the commlet. the commlet calls
  779.         // ComObjIRC.logout() and that disconnects
  780.     }
  781.  
  782.     /**
  783.      * opens an URL to your DocumentHost
  784.      */
  785.     private URL getDataURL( String append ) throws MalformedURLException {
  786.         Applet a = server.getApplet();
  787.         URL url = new URL(a.getDocumentBase(),server.PATH_COMMLET+commletname+"/"+append );
  788.  
  789.         return url;
  790.     }
  791.  
  792.     /**
  793.      * Load a picture with the specified filename!
  794.      * It will be loaded from the BaseDocument's http-server.
  795.      */
  796.     public Image loadImage( String filename ) {
  797.         Applet a = server.getApplet();
  798.         if( a == null ) return null;
  799.  
  800.         try {
  801.             URL url = getDataURL( filename );
  802.             return a.getImage( url );
  803.         } catch( Exception e ) {
  804.             return null;
  805.         }
  806.     }
  807.  
  808.     /**
  809.      * Load an audioclip with the specified filename!
  810.      */
  811.     public AudioClip loadAudioClip( String filename ) {
  812.         Applet a = server.getApplet();
  813.         if( a == null ) return null;
  814.  
  815.         try {
  816.             URL url = getDataURL( filename );
  817.             return a.getAudioClip( url );
  818.         } catch( Exception e ) {
  819.             return null;
  820.         }
  821.     }
  822.  
  823.     /**
  824.      * Open an input-stream to the specified file!
  825.      */
  826.     public InputStream openInputStream( String filename ) {
  827.         Applet a = server.getApplet();
  828.         if( a == null ) return null;
  829.  
  830.         try {
  831.             URL url = getDataURL( filename );
  832.             return url.openStream();
  833.         } catch( Exception e ) {
  834.             return null;
  835.         }
  836.     }
  837.  
  838.     public String toString() {
  839.         return "ComObjIRC for #"+chan.ircName();
  840.     }
  841. }
  842.  
  843. class ReadThreadIRC extends Thread {
  844.     ComObjIRC comobj;
  845.     IrcSocket socket;
  846.  
  847.     ReadThreadIRC( ComObjIRC co, IrcSocket socket ) {
  848.         comobj = co;
  849.         this.socket = socket;
  850.     }
  851.  
  852.     public void run() {
  853.         Msg msg;
  854.  
  855.         // ah well. it can happen that I don't know yet what my commlet is
  856.         // this means to me: wait for it.
  857.         while( true ) {
  858.             if( comobj.getCommlet() != null ) break;
  859.             try {
  860.                 Thread.sleep( 100 );
  861.             } catch( InterruptedException e ) { }
  862.         }
  863.  
  864.         while( true )
  865.         {
  866.             msg = readMsg();
  867.  
  868.             if( msg == null )
  869.                 continue;  // already handled
  870.  
  871.             if( msg.type == Msg.LINE_DROPPED || msg.type == Msg.NOT_ALLOWED ||
  872.                  msg.type == Msg.KICK_USER ) {
  873.                 // Sorry, this is a special case :(
  874.                 // We must stop this thread now!
  875.                 break;
  876.             }
  877.  
  878.             msg.to = comobj.getMyID();
  879.             comobj.getCommlet().handleMsg(msg);
  880.         }
  881.     }
  882.  
  883.     /**
  884.      * read a Msg from my socket. Handle Messages for the ComObj,
  885.      * before the Commlet gets it.
  886.      */
  887.     Msg readMsg() {
  888.         Msg msg;
  889.  
  890.         msg = socket.readMsg();
  891.  
  892.         // that is when we are logging out!
  893.         // tell the read thread to stop.
  894.         if( comobj.do_not_get_any_more_messages == true )
  895.             return new Msg( Msg.LINE_DROPPED );
  896.  
  897.         // here let's delete old cached messages!
  898.         // it's not important where/when I do it, but it
  899.         // has to be done
  900.         comobj.removeOldCachedMsg();
  901.  
  902.         // Here we handle private Message, not
  903.         // to be used by the commlet
  904.         // returns null if msg was handled 
  905.         // else: returns the orginal msg
  906.         return comobj.preHandleMsg( msg );
  907.     }
  908. }
  909.