home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / mozil06.zip / bin / chrome / chatzilla.jar / content / chatzilla / lib / js / irc.js < prev    next >
Text File  |  2000-06-01  |  46KB  |  1,924 lines

  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *   
  3.  * The contents of this file are subject to the Mozilla Public
  4.  * License Version 1.1 (the "License"); you may not use this file
  5.  * except in compliance with the License. You may obtain a copy of
  6.  * the License at http://www.mozilla.org/MPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS
  9.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10.  * implied. See the License for the specific language governing
  11.  * rights and limitations under the License.
  12.  *
  13.  * The Original Code is JSIRC Library
  14.  *
  15.  * The Initial Developer of the Original Code is New Dimensions Consulting,
  16.  * Inc. Portions created by New Dimensions Consulting, Inc. are
  17.  * Copyright (C) 1999 New Dimenstions Consulting, Inc. All
  18.  * Rights Reserved.
  19.  *
  20.  * Contributor(s): 
  21.  *
  22.  *
  23.  * Contributor(s):
  24.  *  Robert Ginda, rginda@ndcico.com, original author
  25.  *
  26.  ****
  27.  *
  28.  * depends on utils.js, events.js, and connection.js
  29.  *
  30.  * IRC(RFC 1459) library.
  31.  * Contains the following classes:
  32.  *
  33.  * CIRCNetwork
  34.  * Networtk object.  Takes care of logging into a "primary" server given a
  35.  * list of potential hostnames for an IRC network.  (among other things.)
  36.  *
  37.  * CIRCServer
  38.  * Server object.  Requires an initialized bsIConnection object for
  39.  * communicating with the irc server.
  40.  * Server.sayTo queues outgoing PRIVMSGs for sending to the server.  Using
  41.  * sayTo takes care not to send lines faster than one every 1.5 seconds.
  42.  * Server.connection.sendData sends raw lines over the connection, avoiding the
  43.  * queue.
  44.  *
  45.  * CIRCUser
  46.  * User objects.  Children of server objects.
  47.  *
  48.  * CIRCChannel
  49.  * Channel object.  Children of server objects.
  50.  *
  51.  * CIRCChanMode
  52.  * Channel mode object.  Children of channel objects
  53.  *
  54.  * CIRCChanUser
  55.  * Channel User objects.  Children of channel objects, with __proto__ set
  56.  * to a CIRCUser object (automatically.)
  57.  *
  58.  * 1999-09-15 rginda@ndcico.com           v1.0
  59.  *
  60.  */
  61.  
  62. function userIsMe (user)
  63. {
  64.     
  65.     switch (user.TYPE)
  66.     {
  67.         case "IRCUser":
  68.             return (user == user.parent.me);
  69.             break;
  70.             
  71.         case "IRCChanUser":
  72.             return (user.__proto__ == user.parent.parent.me);
  73.             break;
  74.             
  75.         default:
  76.             return false;
  77.             
  78.     }
  79. }
  80.  
  81. /*
  82.  * irc network
  83.  */
  84. function CIRCNetwork (name, serverList, eventPump)
  85. {
  86.  
  87.     this.name = name;
  88.     this.serverList = serverList;
  89.     this.eventPump = eventPump;
  90.  
  91. }
  92.  
  93. /** Clients should override this stuff themselves **/
  94. CIRCNetwork.prototype.INITIAL_NICK = "js-irc";
  95. CIRCNetwork.prototype.INITIAL_NAME = "INITIAL_NAME";
  96. CIRCNetwork.prototype.INITIAL_DESC = "INITIAL_DESC";
  97. CIRCNetwork.prototype.INITIAL_CHANNEL = "#jsbot"; 
  98. /* set INITIAL_CHANNEL to "" if you don't want a primary channel */
  99.  
  100. CIRCNetwork.prototype.MAX_CONNECT_ATTEMPTS = 5000;
  101. CIRCNetwork.prototype.stayingPower = false; 
  102.  
  103. CIRCNetwork.prototype.TYPE = "IRCNetwork";
  104.  
  105. CIRCNetwork.prototype.connect =
  106. function net_conenct(pass)
  107. {
  108.  
  109.     var ev = new CEvent ("network", "do-connect", this, "onDoConnect");
  110.     ev.password = pass;
  111.     this.eventPump.addEvent (ev);
  112.  
  113. }
  114.  
  115. CIRCNetwork.prototype.quit =
  116. function net_quit (reason)
  117. {
  118.  
  119.     this.stayingPower = false;
  120.     if (this.isConnected())
  121.         this.primServ.logout (reason);
  122.  
  123. }
  124.  
  125. /*
  126.  * Handles a request to connect to a primary server.
  127.  */
  128. CIRCNetwork.prototype.onDoConnect =
  129. function net_doconnect(e)
  130. {
  131.     var c;
  132.     
  133.     if ((this.primServ) && (this.primServ.connection.isConnected))
  134.         return true;
  135.  
  136.     var attempt = (typeof e.attempt == "undefined") ? 1 : e.attempt + 1;
  137.     var host = (typeof e.lastHost == "undefined") ? 0 : e.lastHost + 1;
  138.  
  139.     if (attempt > this.MAX_CONNECT_ATTEMPTS)
  140.         return false;    
  141.  
  142.     try
  143.     {
  144.         c = new CBSConnection();
  145.     }
  146.     catch (ex)
  147.     {
  148.         dd ("cant make socket.");
  149.         
  150.         var ev = new CEvent ("network", "error", this, "onError");
  151.         ev.meat = "Unable to create socket: " + ex;
  152.         this.eventPump.addEvent (ev);
  153.         return false;
  154.     }
  155.  
  156.     if (host >= this.serverList.length)
  157.         host = 0;
  158.  
  159.     if (c.connect (this.serverList[host].name, this.serverList[host].port,
  160.                    (void 0), true))
  161.     {
  162.         var ev = new CEvent ("network", "connect", this, "onConnect");
  163.         ev.server = this.primServ = new CIRCServer (this, c);
  164.         this.eventPump.addEvent (ev);
  165.     }
  166.     else
  167.     { /* connect failed, try again  */
  168.         var ev = new CEvent ("network", "do-connect", this, "onDoConnect");
  169.         ev.lastHost = host;
  170.         ev.attempt = attempt;
  171.         this.eventPump.addEvent (ev);
  172.     }
  173.  
  174.     return true;
  175.  
  176. }
  177.  
  178. /*
  179.  * What to do when the client connects to it's primary server
  180.  */
  181. CIRCNetwork.prototype.onConnect = 
  182. function net_connect (e)
  183. {
  184.  
  185.     this.primServ = e.server;
  186.     this.primServ.login (this.INITIAL_NICK, this.INITIAL_NAME,
  187.                          this.INITIAL_DESC, e.pass);
  188.     return true;
  189.  
  190. }
  191.  
  192. CIRCNetwork.prototype.isConnected = 
  193. function net_connected (e)
  194. {
  195.  
  196.     return (this.primServ && this.primServ.connection.isConnected);
  197.     
  198. }
  199.  
  200. /*
  201.  * irc server
  202.  */ 
  203. function CIRCServer (parent, connection)
  204. {
  205.  
  206.     this.parent = parent;
  207.     this.connection = connection;
  208.     this.channels = new Object();
  209.     this.users = new Object();
  210.     this.sendQueue = new Array();
  211.     this.lastSend = new Date("1/1/1980");
  212.     this.sendsThisRound = 0;
  213.     this.savedLine = "";
  214.     this.lag = -1;    
  215.     this.usersStable = true;
  216.  
  217.     if (typeof connection.startAsyncRead == "function")
  218.         connection.startAsyncRead(this);
  219.     else
  220.         this.parent.eventPump.addEvent(new CEvent ("server", "poll", this,
  221.                                                    "onPoll"));
  222.     
  223. }
  224.  
  225. CIRCServer.prototype.MAX_LINES_PER_SEND = 5;
  226. CIRCServer.prototype.MS_BETWEEN_SENDS = 1500;
  227. CIRCServer.prototype.READ_TIMEOUT = 2000;
  228. CIRCServer.prototype.TOO_MANY_LINES_MSG = "\01ACTION has said too much\01";
  229. CIRCServer.prototype.VERSION_RPLY = "JS-IRC Library v0.01, " +
  230.     "Copyright (C) 1999 Robert Ginda; rginda@ndcico.com";
  231. CIRCServer.prototype.DEFAULT_REASON = "no reason";
  232.  
  233. CIRCServer.prototype.TYPE = "IRCServer";
  234.  
  235. CIRCServer.prototype.flushSendQueue =
  236. function serv_flush()
  237. {
  238.  
  239.     this.sendQueue.length = 0;
  240.     dd("sendQueue flushed.");
  241.  
  242.     return true;
  243.  
  244. }
  245.  
  246. CIRCServer.prototype.login =
  247. function serv_login(nick, name, desc, pass)
  248. {
  249.  
  250.     this.me = new CIRCUser (this, nick, name);
  251.     if (pass)
  252.        this.sendData ("PASS " + pass + "\n");
  253.     this.sendData ("NICK " + nick + "\n");
  254.     this.sendData ("USER " + name + " foo bar :" + desc + "\n");
  255.     
  256. }
  257.  
  258. CIRCServer.prototype.logout =
  259. function serv_logout(reason)
  260. {
  261.     
  262.     if (typeof reason == "undefined") reason = this.DEFAULT_REASON;
  263.  
  264.     this.connection.sendData ("QUIT :" + reason + "\n");
  265.     this.connection.disconnect();
  266.  
  267. }
  268.  
  269. CIRCServer.prototype.addChannel =
  270. function serv_addchan (name)
  271. {
  272.  
  273.     return new CIRCChannel (this, name);
  274.     
  275. }
  276.     
  277. CIRCServer.prototype.addUser =
  278. function serv_addusr (nick, name, host)
  279. {
  280.  
  281.     return new CIRCUser (this, nick, name, host);
  282.     
  283. }
  284.     
  285. CIRCServer.prototype.getChannelsLength =
  286. function serv_chanlen()
  287. {
  288.     var i = 0;
  289.  
  290.     for (var p in this.channels)
  291.         i++;
  292.  
  293.     return i;
  294.     
  295. }
  296.  
  297. CIRCServer.prototype.getUsersLength =
  298. function serv_chanlen()
  299. {
  300.     var i = 0;
  301.  
  302.     for (var p in this.users)
  303.         i++;
  304.  
  305.     return i;
  306.     
  307. }
  308.  
  309. CIRCServer.prototype.sendData =
  310. function serv_senddata (msg)
  311. {
  312.     
  313.     this.queuedSendData (msg);
  314.         
  315. }
  316.  
  317. CIRCServer.prototype.queuedSendData =
  318. function serv_senddata (msg)
  319. {
  320.  
  321.     if (this.sendQueue.length == 0)
  322.         this.parent.eventPump.addEvent (new CEvent ("server", "senddata",
  323.                                                     this, "onSendData"));
  324.     arrayInsertAt (this.sendQueue, 0, msg);
  325.         
  326. }
  327.  
  328. /*
  329.  * Takes care not to let more than MAX_LINES_PER_SEND lines out per
  330.  * cycle.  Cycle's are defined as the time between onPoll calls.
  331.  */
  332. CIRCServer.prototype.messageTo =
  333. function serv_messto (code, target, msg, ctcpCode)
  334. {
  335.     var lines = String(msg).split ("\n");
  336.     var sendable = 0, i;
  337.     var pfx = "", sfx = "";
  338.  
  339.     if (this.sendsThisRound > this.MAX_LINES_PER_SEND)
  340.         return false;
  341.  
  342.     if (ctcpCode)
  343.     {
  344.         pfx = "\01" + ctcpCode + " ";
  345.         sfx = "\01";
  346.     }
  347.  
  348.     for (i in lines)
  349.         if ((lines[i] != "") || ctcpCode) sendable++;
  350.  
  351.     for (i in lines)
  352.     {
  353.         if (((this.sendsThisRound == this.MAX_LINES_PER_SEND - 1) &&
  354.              (sendable > this.MAX_LINES_PER_SEND)) ||
  355.             (this.sendsThisRound == this.MAX_LINES_PER_SEND))
  356.         {
  357.             this.sendData ("PRIVMSG " + target + " :" +
  358.                            this.TOO_MANY_LINES_MSG + "\n");
  359.             this.sendsThisRound++;
  360.             return true;
  361.         }
  362.             
  363.         if ((lines[i] != "") || ctcpCode)
  364.         {
  365.             this.sendsThisRound++;
  366.             var line = code + " " + target + " :" + pfx + lines[i] + sfx;
  367.             //dd ("-*- irc sending '" +  line + "'");
  368.             this.sendData (line + "\n");
  369.         }
  370.         
  371.     }
  372.  
  373.     return true;        
  374. }
  375.  
  376. CIRCServer.prototype.sayTo = 
  377. function serv_sayto (target, msg)
  378. {
  379.  
  380.     this.messageTo ("PRIVMSG", target, msg);
  381.  
  382. }
  383.  
  384. CIRCServer.prototype.noticeTo = 
  385. function serv_noticeto (target, msg)
  386. {
  387.  
  388.     this.messageTo ("NOTICE", target, msg);
  389.  
  390. }
  391.  
  392. CIRCServer.prototype.actTo = 
  393. function serv_actto (target, msg)
  394. {
  395.  
  396.     this.messageTo ("PRIVMSG", target, msg, "ACTION");
  397.  
  398. }
  399.  
  400. CIRCServer.prototype.ctcpTo = 
  401. function serv_ctcpto (target, code, msg, method)
  402. {
  403.     if (typeof msg == "undefined")
  404.         msg = "";
  405.  
  406.     if (typeof method == "undefined")
  407.         method = "PRIVMSG";
  408.     
  409.      
  410.     this.messageTo (method, target, msg, code);
  411.  
  412. }
  413.  
  414. /**
  415.  * Abstracts the whois command.
  416.  *
  417.  * @param target        intended user(s).
  418.  */
  419. CIRCServer.prototype.whois = 
  420. function serv_whois (target) 
  421. {
  422.  
  423.     this.sendData ("WHOIS " + target + "\n");
  424.  
  425. }
  426.  
  427. CIRCServer.prototype.onDisconnect = 
  428. function serv_disconnect(e)
  429. {
  430.  
  431.   if ((this.parent.primServ == this) && (this.parent.stayingPower))
  432.   { /* fell off primary server, reconnect to any host in the serverList */
  433.     var ev = new CEvent ("network", "do-connect", this.parent,
  434.                              "onDoConnect");
  435.     this.parent.eventPump.addEvent (ev);
  436.   }
  437.  
  438.   return true;
  439.  
  440. }
  441.  
  442. CIRCServer.prototype.onSendData =
  443. function serv_onsenddata (e)
  444. {
  445.     var d = new Date();
  446.  
  447.     if (!this.connection.isConnected)
  448.     {
  449.         var ev = new CEvent ("server", "disconnect", this,
  450.                              "onDisconnect");
  451.         ev.reason = "unknown";
  452.         this.parent.eventPump.addEvent (ev);
  453.         return false;
  454.     }
  455.  
  456.     this.sendsThisRound = 0;
  457.  
  458.     if (((d - this.lastSend) >= this.MS_BETWEEN_SENDS) &&
  459.         this.sendQueue.length > 0)                            
  460.     {
  461.         var s = this.sendQueue.pop();  
  462.       
  463.         if (s)
  464.         {
  465.             //dd ("queued send: " + s);
  466.             this.connection.sendData (s);
  467.             this.lastSend = d;
  468.         }
  469.  
  470.     }
  471.     else
  472.         this.parent.eventPump.addEvent (new CEvent ("event-pump", "yield",
  473.                                                     null, ""));
  474.  
  475.     if (this.sendQueue.length > 0)
  476.         this.parent.eventPump.addEvent (new CEvent ("server", "senddata",
  477.                                                     this, "onSendData"));
  478.     
  479. }
  480.  
  481. CIRCServer.prototype.onPoll = 
  482. function serv_poll(e)
  483. {
  484.     var lines;
  485.     var ex;
  486.     
  487.     try
  488.     {
  489.         line = this.connection.readData(this.READ_TIMEOUT);
  490.     }
  491.     catch (ex)
  492.     {
  493.         dd ("*** Caught exception " + ex + " reading from server " +
  494.             this.connection.host);
  495.         if (jsenv.HAS_RHINO && (ex instanceof java.lang.ThreadDeath))
  496.         {
  497.             dd("### catching a ThreadDeath");
  498.             throw(ex);
  499.         }
  500.         else if (typeof ex != "undefined")
  501.         {
  502.              var ev = new CEvent ("server", "disconnect", this,
  503.                                   "onDisconnect");
  504.              ev.reason = "error";
  505.              ev.exception = ex;
  506.              this.parent.eventPump.addEvent (ev);
  507.              return false;
  508.         }
  509.         else
  510.             line = ""
  511.     }
  512.         
  513.     this.parent.eventPump.addEvent (new CEvent ("server", "poll", this,
  514.                                                 "onPoll"));
  515.  
  516.     if (line)
  517.     {
  518.         var ev = new CEvent ("server", "data-available", this,
  519.                              "onDataAvailable");
  520.         ev.line = line;
  521.         this.parent.eventPump.addEvent (ev);
  522.     }
  523.     
  524. }
  525.  
  526. CIRCServer.prototype.onDataAvailable = 
  527. function serv_ppline(e)
  528. {
  529.     var line = e.line;
  530.     
  531.     if (line == "")
  532.         return false;
  533.     
  534.     var incomplete = (line[line.length] != '\n');
  535.     var lines = line.split("\n");
  536.  
  537.     if (this.savedLine)
  538.     {
  539.         lines[0] = this.savedLine + lines[0];
  540.         this.savedLine = "";
  541.     }
  542.     
  543.     if (incomplete)
  544.         this.savedLine = lines.pop();
  545.     
  546.     for (i in lines)
  547.     {
  548.         var ev = new CEvent("server", "rawdata", this, "onRawData");
  549.         ev.data = lines[i].replace(/\r/g, "");
  550.         this.parent.eventPump.addEvent (ev);
  551.     }
  552. }
  553.  
  554. /*
  555.  * onRawData begins shaping the event by parsing the IRC message at it's
  556.  * simplest level.  After onRawData, the event will have the following
  557.  * properties:
  558.  * name           value
  559.  *
  560.  * set............"server"
  561.  * type..........."parsedata"
  562.  * destMethod....."onParsedData"
  563.  * destObject.....server (this)
  564.  * server.........server (this)
  565.  * connection.....CBSConnection (this.connection)
  566.  * source.........the <prefix> of the message (if it exists)
  567.  * user...........user object initialized with data from the message <prefix>
  568.  * params.........array containing the <middle> parameters of the message
  569.  * code...........the first <middle> parameter (most messages have this)
  570.  * meat...........the <trailing> parameter of the message
  571.  *
  572.  * See Section 2.3.1 of RFC 1459 for details on <prefix>, <middle> and
  573.  * <trailing> tokens.
  574.  */
  575. CIRCServer.prototype.onRawData = 
  576. function serv_onRawData(e)
  577. {
  578.     var ary;
  579.     var l = e.data;
  580.  
  581.     if (l[0] == ":")
  582.     {
  583.         ary = l.match (/:(\S+)\s(.*)/);
  584.         e.source = ary[1];
  585.         l = ary[2];
  586.         ary = e.source.match (/(\S+)!(\S+)@(.*)/);
  587.         if (ary)
  588.         {
  589.             e.user = new CIRCUser(this, ary[1], ary[2], ary[3]);
  590.         }
  591.         else
  592.         {
  593.             ary = e.source.match (/(\S+)@(.*)/);
  594.             if (ary)
  595.             {
  596.                 e.user = new CIRCUser(this, "", ary[1], ary[2]);
  597.             }
  598.         }
  599.     }
  600.  
  601.     e.server = this;
  602.  
  603.     var sep = l.indexOf(":");
  604.  
  605.     if (sep != -1) /* <trailing> param, if there is one */
  606.         e.meat = l.substr (sep + 1, l.length);
  607.     else
  608.         e.meat = "";
  609.  
  610.     if (sep != -1)
  611.         e.params = l.substr(0, sep).split(" ");
  612.     else
  613.         e.params = l.split(" ");
  614.     e.code = e.params[0].toUpperCase();
  615.     if (e.params[e.params.length - 1] == "")
  616.         e.params.length--;
  617.  
  618.     e.type = "parseddata";
  619.     e.destObject = this;
  620.     e.destMethod = "onParsedData";
  621.     
  622.     return true;
  623.     
  624. }
  625.  
  626. /*
  627.  * onParsedData forwards to next event, based on |e.code|
  628.  */
  629. CIRCServer.prototype.onParsedData = 
  630. function serv_onParsedData(e)
  631. {
  632.  
  633.     e.type = e.code.toLowerCase();
  634.     e.destMethod = "on" + e.code[0].toUpperCase() +
  635.         e.code.substr (1, e.code.length).toLowerCase();
  636.  
  637.     if (typeof this[e.destMethod] == "function")
  638.         e.destObject = this;
  639.     else
  640.     {
  641.         e.set = "network";
  642.         e.destObject = this.parent;
  643.     }
  644.  
  645.     return true;
  646.     
  647. }
  648.  
  649. /* User changed topic */
  650. CIRCServer.prototype.onTopic = 
  651. function serv_topic (e)
  652. {
  653.  
  654.     e.channel = new CIRCChannel (this, e.params[1]);
  655.     e.channel.topicBy = e.user.nick;
  656.     e.channel.topicDate = new Date();
  657.     e.channel.topic = e.meat;
  658.     e.destObject = e.channel;
  659.     e.set = "channel";
  660.  
  661.     return true;
  662.     
  663. }
  664.  
  665. /* Successful login */
  666. CIRCServer.prototype.on001 =
  667. function serv_001 (e)
  668. {
  669.  
  670.     /* servers wont send a nick change notification if user was forced
  671.      * to change nick while logging in (eg. nick already in use.)  We need
  672.      * to verify here that what the server thinks our name is, matches what
  673.      * we think it is.  If not, the server wins.
  674.      */
  675.     if (e.params[1] != e.server.me.properNick)
  676.     {
  677.         renameProperty (e.server.users, e.server.me.nick, e.params[1]);
  678.         e.server.me.changeNick(e.params[1]);
  679.     }
  680.     
  681.     if (this.parent.INITIAL_CHANNEL)
  682.     {
  683.         this.parent.primChan = this.addChannel (this.parent.INITIAL_CHANNEL);
  684.         this.parent.primChan.join();
  685.     }
  686.  
  687.     e.destObject = this.parent;
  688.     e.set = "network";
  689. }
  690.  
  691.  
  692. /* TOPIC reply */
  693. CIRCServer.prototype.on332 =
  694. function serv_332 (e)
  695. {
  696.  
  697.     e.channel = new CIRCChannel (this, e.params[2]);
  698.     e.channel.topic = e.meat;
  699.     e.destObject = e.channel;
  700.     e.set = "channel";
  701.  
  702.     return true;
  703.     
  704. }
  705.  
  706. /* topic information */
  707. CIRCServer.prototype.on333 = 
  708. function serv_333 (e)
  709. {
  710.  
  711.     e.channel = new CIRCChannel (this, e.params[2]);
  712.     e.channel.topicBy = e.params[3];
  713.     e.channel.topicDate = new Date(Number(e.params[4]) * 1000);
  714.     e.destObject = e.channel;
  715.     e.set = "channel";
  716.  
  717.     return true;
  718.  
  719. }
  720.  
  721. /* name reply */
  722. CIRCServer.prototype.on353 = 
  723. function serv_353 (e)
  724. {
  725.     
  726.     e.channel = new CIRCChannel (this, e.params[3]);
  727.     if (e.channel.usersStable)
  728.         e.channel.users = new Object();
  729.     
  730.     e.destObject = e.channel;
  731.     e.set = "channel";
  732.  
  733.     var nicks = e.meat.split (" ");
  734.  
  735.     for (var n in nicks)
  736.     {
  737.         nick = nicks[n];
  738.         if (nick == "")
  739.             break;
  740.         
  741.         switch (nick[0])
  742.         {
  743.             case "@":
  744.                 e.user = new CIRCChanUser (e.channel,
  745.                                            nick.substr(1, nick.length),
  746.                                            true, (void 0));
  747.                 break;
  748.             
  749.             case "+":
  750.                 e.user = new CIRCChanUser (e.channel,
  751.                                            nick.substr(1, nick.length),
  752.                                            (void 0), true);
  753.                 break;
  754.             
  755.             default:
  756.                 e.user = new CIRCChanUser (e.channel, nick);
  757.                 break;
  758.         }
  759.  
  760.     }
  761.  
  762.     return true;
  763.     
  764. }
  765.  
  766. /* end of names */
  767. CIRCServer.prototype.on366 = 
  768. function serv_366 (e)
  769. {
  770.  
  771.     e.channel = new CIRCChannel (this, e.params[2]);
  772.     e.destObject = e.channel;
  773.     e.set = "channel";
  774.     e.channel.usersStable = true;
  775.  
  776.     return true;
  777.     
  778. }    
  779.  
  780. /* channel time stamp? */
  781. CIRCServer.prototype.on329 = 
  782. function serv_329 (e)
  783. {
  784.  
  785.     e.channel = new CIRCChannel (this, e.params[2]);
  786.     e.destObject = e.channel;
  787.     e.set = "channel";
  788.     e.channel.timeStamp = new Date (Number(e.params[3]) * 1000);
  789.     
  790.     return true;
  791.     
  792. }
  793.     
  794. /* channel mode reply */
  795. CIRCServer.prototype.on324 = 
  796. function serv_324 (e)
  797. {
  798.  
  799.     e.channel = new CIRCChannel (this, e.params[2]);
  800.     e.destObject = this;
  801.     e.type = "chanmode";
  802.     e.destMethod = "onChanMode";
  803.  
  804.     return true;
  805.  
  806. }
  807.  
  808. /* user changed the mode */
  809. CIRCServer.prototype.onMode = 
  810. function serv_mode (e)
  811. {
  812.  
  813.     e.destObject = this;
  814.     
  815.     if ((e.params[1][0] == "#") || (e.params[1][0] == "&"))
  816.     {
  817.         e.channel = new CIRCChannel (this, e.params[1]);
  818.         if (e.user)
  819.             e.user = new CIRCChanUser (e.channel, e.user.nick);
  820.         e.type = "chanmode";
  821.         e.destMethod = "onChanMode";
  822.     }
  823.     else
  824.     {
  825.         e.type = "usermode";
  826.         e.destMethod = "onUserMode";
  827.     }
  828.     
  829.     return true;
  830.     
  831. }
  832.  
  833. CIRCServer.prototype.onUserMode = 
  834. function serv_usermode (e)
  835. {
  836.  
  837.     return true;
  838.     
  839. }
  840.  
  841. CIRCServer.prototype.onChanMode = 
  842. function serv_chanmode (e)
  843. {
  844.     var modifier = "";
  845.     var params_eaten = 0;
  846.     var BASE_PARAM;
  847.  
  848.     if (e.code.toUpperCase() == "MODE")
  849.         BASE_PARAM = 2;
  850.     else
  851.         if (e.code == "324")
  852.             BASE_PARAM = 3;
  853.         else
  854.         {
  855.             dd ("** INVALID CODE in ChanMode event **");
  856.             return false;
  857.         }
  858.  
  859.     var mode_str = e.params[BASE_PARAM];
  860.     params_eaten++;
  861.  
  862.     e.modeStr = mode_str;
  863.     e.usersAffected = new Array();
  864.  
  865.     for (var i = 0; i < mode_str.length ; i++)
  866.     {
  867.         switch (mode_str[i])
  868.         {
  869.             case "+":
  870.             case "-":
  871.                 modifier = mode_str[i];
  872.                 break;
  873.  
  874.             /* user modes */
  875.             case "o": /* operator */
  876.                 if (modifier == "+")
  877.                 {
  878.                     var nick = e.params[BASE_PARAM + params_eaten];
  879.                     var user = new CIRCChanUser (e.channel, nick, true);
  880.                     params_eaten++;
  881.                     e.usersAffected.push (user);
  882.                 }
  883.                 else
  884.                     if (modifier == "-")
  885.                     {
  886.                         var nick = e.params[BASE_PARAM + params_eaten];
  887.                         var user = new CIRCChanUser (e.channel, nick, false);
  888.                         params_eaten++;
  889.                         e.usersAffected.push (user);
  890.                     }
  891.                 break;
  892.                         
  893.             case "v": /* voice */
  894.                 if (modifier == "+")
  895.                 {
  896.                     var nick = e.params[BASE_PARAM + params_eaten];
  897.                     var user = new CIRCChanUser (e.channel, nick, (void 0),
  898.                                                  true);
  899.                     params_eaten++;
  900.                     e.usersAffected.push (user);
  901.                 }
  902.                 else
  903.                     if (modifier == "-")
  904.                     {
  905.                         var nick = e.params[BASE_PARAM + params_eaten];
  906.                         var user = new CIRCChanUser (e.channel, nick, (void 0),
  907.                                                      false);
  908.                         params_eaten++;
  909.                         e.usersAffected.push (user);
  910.                     }
  911.                 break;
  912.                 
  913.             case "b": /* ban */
  914.                 var ban = e.params[BASE_PARAM + params_eaten];
  915.                 params_eaten++;
  916.  
  917.                 if ((modifier == "+") &&
  918.                     (typeof e.channel.bans[ban] == "undefined"))
  919.                 {
  920.                     e.channel.bans[ban] = {host: ban};
  921.                     ban_evt = new CEvent ("channel", "ban", e.channel,
  922.                                           "onBan");
  923.                     ban_evt.channel = e.channel;
  924.                     ban_evt.ban = ban;
  925.                     ban_evt.source = e.user;
  926.                     this.parent.eventPump.addEvent (e);
  927.                 }
  928.                 else
  929.                     if (modifier == "-")
  930.                         delete e.channel.bans[ban];
  931.                 break;
  932.  
  933.                 
  934.             /* channel modes */
  935.             case "l": /* limit */
  936.                 if (modifier == "+")
  937.                 {
  938.                     var limit = e.params[BASE_PARAM + params_eaten];
  939.                     params_eaten++;
  940.                     e.channel.mode.limit = limit;
  941.                 }
  942.                 else
  943.                     if (modifier == "-")
  944.                         e.channel.mode.limit = -1;
  945.                 break;
  946.  
  947.             case "k": /* key */
  948.                 var key = e.params[BASE_PARAM + params_eaten];
  949.                 params_eaten++;
  950.  
  951.                 if (modifier == "+")
  952.                     e.channel.mode.key = key;
  953.                 else
  954.                     if (modifier == "-")
  955.                         e.channel.mode.key = "";
  956.                 break;
  957.  
  958.             case "m": /* moderated */
  959.                 if (modifier == "+")
  960.                     e.channel.mode.moderated = true;
  961.                 else
  962.                     if (modifier == "-")
  963.                         e.channel.mode.moderated = false;
  964.                 break;
  965.  
  966.             case "n": /* no outside messages */
  967.                 if (modifier == "+")
  968.                     e.channel.mode.publicMessages = false;
  969.                 else
  970.                     if (modifier == "-")
  971.                         e.channel.mode.publicMessages = true;
  972.                 break;
  973.  
  974.             case "t": /* topic */
  975.                 if (modifier == "+")
  976.                     e.channel.mode.publicTopic = false;
  977.                 else
  978.                     if (modifier == "-")
  979.                         e.channel.mode.publicTopic = true;
  980.                 break;
  981.                 
  982.             case "i": /* invite */
  983.                 if (modifier == "+")
  984.                     e.channel.mode.invite = true;
  985.                 else
  986.                     if (modifier == "-")
  987.                         e.channel.mode.invite = false;
  988.                 break;
  989.  
  990.             case "s": /* secret */
  991.                 if (modifier == "+")
  992.                     e.channel.mode.secret  = true;
  993.                 else
  994.                     if (modifier == "-")
  995.                         e.channel.mode.secret = false;
  996.                 break;
  997.                 
  998.             case "p": /* private */
  999.                 if (modifier == "+")
  1000.                     e.channel.mode.pvt = true;
  1001.                 else
  1002.                     if (modifier == "-")
  1003.                         e.channel.mode.pvt = false;
  1004.                 break;
  1005.                 
  1006.         }
  1007.     }
  1008.  
  1009.     e.destObject = e.channel;
  1010.     e.set = "channel";
  1011.     return true;
  1012.  
  1013. }
  1014.  
  1015. CIRCServer.prototype.onNick = 
  1016. function serv_nick (e)
  1017. {
  1018.     /* Some irc networks send the new nick in the meat, some send it in param[1]
  1019.      * Handle both cases. */
  1020.     var newNick = (e.meat) ? e.meat : e.params[1]; 
  1021.     var newKey = newNick.toLowerCase();
  1022.     var oldKey = e.user.nick;
  1023.     
  1024.     renameProperty (e.server.users, oldKey, newKey);
  1025.     e.oldNick = e.user.properNick;
  1026.     e.user.changeNick(newNick);
  1027.     
  1028.     for (var c in e.server.channels)
  1029.     {
  1030.         var cuser = e.server.channels[c].users[oldKey];
  1031.  
  1032.         if (typeof cuser != "undefined")
  1033.         {
  1034.             renameProperty (e.server.channels[c].users, oldKey, newKey);
  1035.             var ev = new CEvent ("channel", "nick", e.server.channels[c],
  1036.                                  "onNick");
  1037.             ev.channel = e.server.channels[c];
  1038.             ev.user = cuser;
  1039.             ev.server = ev.channel.parent;
  1040.             ev.oldNick = e.oldNick;
  1041.             this.parent.eventPump.addEvent(ev);
  1042.         }
  1043.         
  1044.     }
  1045.  
  1046.     e.destObject = e.user;
  1047.     e.set = "user";    
  1048.  
  1049.     return true;
  1050.     
  1051. }
  1052.  
  1053. CIRCServer.prototype.onQuit = 
  1054. function serv_quit (e)
  1055. {
  1056.  
  1057.     for (var c in e.server.channels)
  1058.     {
  1059.         if (e.server.channels[c].users[e.user.nick])
  1060.         {
  1061.             var ev = new CEvent ("channel", "quit", e.server.channels[c],
  1062.                                  "onQuit");
  1063.             ev.user = e.server.channels[c].users[e.user.nick];
  1064.             ev.channel = e.server.channels[c];
  1065.             ev.server = ev.channel.parent;
  1066.             ev.reason = e.meat;
  1067.             this.parent.eventPump.addEvent(ev);
  1068.             delete e.server.channels[c].users[e.user.nick];
  1069.         }
  1070.     }
  1071.  
  1072.     this.users[e.user.nick].lastQuitMessage = e.meat;
  1073.     this.users[e.user.nick].lastQuitDate = new Date;
  1074.  
  1075.     e.reason = e.meat;
  1076.     e.destObject = e.user;
  1077.     e.set = "user";
  1078.  
  1079.     return true;
  1080.  
  1081. }
  1082.  
  1083. CIRCServer.prototype.onPart = 
  1084. function serv_part (e)
  1085. {
  1086.  
  1087.     e.channel = new CIRCChannel (this, e.params[1]);    
  1088.     e.user = new CIRCChanUser (e.channel, e.user.nick);
  1089.     e.channel.removeUser(e.user.nick);
  1090.     e.destObject = e.channel;
  1091.     e.set = "channel";
  1092.  
  1093.     return true;
  1094.     
  1095. }
  1096.  
  1097. CIRCServer.prototype.onKick = 
  1098. function serv_kick (e)
  1099. {
  1100.  
  1101.     e.channel = new CIRCChannel (this, e.params[1]);
  1102.     e.lamer = new CIRCChanUser (e.channel, e.params[2]);
  1103.     delete e.channel.users[e.lamer.nick];
  1104.     e.reason = e.meat;
  1105.     e.destObject = e.channel;
  1106.     e.set = "channel"; 
  1107.  
  1108.     return true;
  1109.     
  1110. }
  1111.  
  1112. CIRCServer.prototype.onJoin = 
  1113. function serv_join (e)
  1114. {
  1115.  
  1116.     e.channel = new CIRCChannel (this, e.meat);
  1117.     if (e.user == this.me)
  1118.         e.server.sendData ("MODE " + e.channel.name + "\n" /* +
  1119.                            "BANS " + e.channel.name + "\n" */);
  1120.     e.user = new CIRCChanUser (e.channel, e.user.nick);
  1121.  
  1122.     e.destObject = e.channel;
  1123.     e.set = "channel";
  1124.  
  1125.     return true;
  1126.     
  1127. }
  1128.  
  1129. CIRCServer.prototype.onPing = 
  1130. function serv_ping (e)
  1131. {
  1132.  
  1133.     /* non-queued send, so we can calcualte lag */
  1134.     this.connection.sendData ("PONG :" + e.meat + "\n");
  1135.     this.connection.sendData ("PING :" + e.meat + "\n");
  1136.     this.lastPing = this.lastPingSent = new Date();
  1137.  
  1138.     e.destObject = this.parent;
  1139.     e.set = "network";
  1140.  
  1141.     return true;
  1142.     
  1143. }
  1144.  
  1145. CIRCServer.prototype.onPong = 
  1146. function serv_pong (e)
  1147. {
  1148.  
  1149.     if (this.lastPingSent)
  1150.         this.lag = (new Date() - this.lastPingSent) / 1000;
  1151.  
  1152.     delete this.lastPingSent;
  1153.     
  1154.     e.destObject = this.parent;
  1155.     e.set = "network";
  1156.  
  1157.     return true;
  1158.     
  1159. }
  1160.  
  1161. CIRCServer.prototype.onNotice = 
  1162. function serv_notice (e)
  1163. {
  1164.  
  1165.     if (!e.user)
  1166.     {
  1167.         e.set = "network";
  1168.         e.destObject = this.parent;
  1169.         return true;
  1170.     }
  1171.         
  1172.     if ((e.params[1][0] == "#") || (e.params[1][0] == "&"))
  1173.     {
  1174.         e.channel = new CIRCChannel(this, e.params[1]);
  1175.         e.user = new CIRCChanUser (e.channel, e.user.nick);
  1176.         e.replyTo = e.channel;
  1177.         e.set = "channel";
  1178.     }
  1179.     else
  1180.     {
  1181.         e.set = "user";
  1182.         e.replyTo = e.user; /* send replys to the user who sent the message */
  1183.     }
  1184.  
  1185.     e.destObject = e.replyTo;
  1186.  
  1187.     return true;
  1188.     
  1189. }
  1190.  
  1191. CIRCServer.prototype.onPrivmsg = 
  1192. function serv_privmsg (e)
  1193. {
  1194.     
  1195.     /* setting replyTo provides a standard place to find the target for     */
  1196.     /* replys associated with this event.                                   */
  1197.     if ((e.params[1][0] == "#") || (e.params[1][0] == "&"))
  1198.     {
  1199.         e.channel = new CIRCChannel(this, e.params[1]);
  1200.         e.user = new CIRCChanUser (e.channel, e.user.nick);
  1201.         e.replyTo = e.channel;
  1202.         e.set = "channel";
  1203.     }
  1204.     else
  1205.     {
  1206.         e.set = "user";
  1207.         e.replyTo = e.user; /* send replys to the user who sent the message */
  1208.     }
  1209.  
  1210.     if (e.meat.search (/\x01.*\x01/i) != -1)
  1211.     {
  1212.         e.type = "ctcp";
  1213.         e.destMethod = "onCTCP";
  1214.         e.set = "server";
  1215.         e.destObject = this;
  1216.     }
  1217.     else
  1218.         e.destObject = e.replyTo;
  1219.  
  1220.     return true;
  1221.     
  1222. }
  1223.  
  1224. CIRCServer.prototype.onCTCP = 
  1225. function serv_ctcp (e)
  1226. {
  1227.     var ary = e.meat.match (/^\x01(\S+) ?(.*)\x01$/i);
  1228.  
  1229.     if (ary == null)
  1230.         return false;
  1231.  
  1232.     e.CTCPData = ary[2] ? ary[2] : "";
  1233.  
  1234.     e.CTCPCode = ary[1].toLowerCase();
  1235.     e.type = "ctcp-" + e.CTCPCode;
  1236.     e.destMethod = "onCTCP" + ary[1][0].toUpperCase() +
  1237.         ary[1].substr (1, ary[1].length).toLowerCase();
  1238.  
  1239.     if (typeof this[e.destMethod] != "function")
  1240.     { /* if there's no place to land the event here, try to forward it */
  1241.         e.destObject = e.replyTo;
  1242.         e.set = (e.replyTo == e.user) ? "user" : "channel";
  1243.         
  1244.         if (typeof e.replyTo[e.destMethod] != "function")
  1245.         { /* if there's no place to forward it, send it to unknownCTCP */
  1246.             e.type = "unk-ctcp";
  1247.             e.destMethod = "onUnknownCTCP";
  1248.         }
  1249.     }
  1250.     else
  1251.         e.destObject = this;
  1252.  
  1253.     return true;
  1254.     
  1255. }
  1256.  
  1257. CIRCServer.prototype.onCTCPVersion = 
  1258. function serv_cver (e)
  1259. {
  1260.     var lines = e.server.VERSION_RPLY.split ("\n");
  1261.     
  1262.     for (var i in lines)
  1263.         e.user.notice ("\01VERSION " + lines[i] + "\01");
  1264.     
  1265.     e.destObject = e.replyTo;
  1266.     e.set = (e.replyTo == e.user) ? "user" : "channel";
  1267.  
  1268.     return true;
  1269.     
  1270. }
  1271.  
  1272. CIRCServer.prototype.onCTCPPing = 
  1273. function serv_cping (e)
  1274. {
  1275.  
  1276.     /* non-queued send */
  1277.     e.server.connection.sendData ("NOTICE " + e.user.nick + " :\01PING " +
  1278.                                   e.CTCPData + "\01\n");
  1279.     e.destObject = e.replyTo;
  1280.     e.set = (e.replyTo == e.user) ? "user" : "channel";
  1281.  
  1282.     return true;
  1283.     
  1284. }
  1285.  
  1286. CIRCServer.prototype.onCTCPDcc = 
  1287. function serv_dcc (e)
  1288. {
  1289.  
  1290.     var ary = e.CTCPData.match (/(\S+)? ?(.*)/);
  1291.     
  1292.     e.DCCData = ary[2];
  1293.     e.type = "dcc-" + ary[1].toLowerCase();
  1294.     e.destMethod = "onDCC" + ary[1][0].toUpperCase() +
  1295.         ary[1].substr (1, ary[1].length).toLowerCase();
  1296.  
  1297.     if (typeof this[e.destMethod] != "function")
  1298.     { /* if there's no place to land the event here, try to forward it */
  1299.         e.destObject = e.replyTo;
  1300.         e.set = (e.replyTo == e.user) ? "user" : "channel";
  1301.     }
  1302.     else
  1303.         e.destObject = this;
  1304.  
  1305.     return true;
  1306.  
  1307. }
  1308.  
  1309. CIRCServer.prototype.onDCCChat = 
  1310. function serv_dccchat (e)
  1311. {
  1312.     var ary = e.DCCData.match (/(chat) (\d+) (\d+)/i);
  1313.  
  1314.     if (ary == null)
  1315.         return false;
  1316.  
  1317.     e.id = ary[2];
  1318.     e.port = ary[3];
  1319.     e.destObject = e.replyTo;
  1320.     e.set = (e.replyTo == e.user) ? "user" : "channel";
  1321.     
  1322.     return true;
  1323.     
  1324. }
  1325.  
  1326. CIRCServer.prototype.onDCCSend = 
  1327. function serv_dccsend (e)
  1328. {
  1329.  
  1330.     var ary = e.DCCData.match (/(\S+) (\d+) (\d+) (\d+)/);
  1331.  
  1332.     if (ary == null)
  1333.         return false;
  1334.     
  1335.     e.file = ary[1];
  1336.     e.id   = ary[2];
  1337.     e.port = ary[3];
  1338.     e.size = ary[4];
  1339.     e.destObject = e.replyTo;
  1340.     e.set = (e.replyTo == e.user) ? "user" : "channel";
  1341.  
  1342.     return true;
  1343.     
  1344. }
  1345.  
  1346. /*
  1347.  * channel
  1348.  */
  1349.  
  1350. function CIRCChannel (parent, name)
  1351. {
  1352.  
  1353.     name = name.toLowerCase();
  1354.     
  1355.     var existingChannel = parent.channels[name];
  1356.  
  1357.     if (typeof existingChannel == "object")
  1358.         return existingChannel;
  1359.     
  1360.     this.parent = parent;
  1361.     this.name = name;
  1362.     this.users = new Object();
  1363.     this.bans = new Object();
  1364.     this.mode = new CIRCChanMode (this);
  1365.     
  1366.     parent.channels[name] = this;
  1367.  
  1368. }
  1369.  
  1370. CIRCChannel.prototype.TYPE = "IRCChannel";
  1371.     
  1372. CIRCChannel.prototype.addUser = 
  1373. function chan_adduser (nick, isOp, isVoice)
  1374. {
  1375.  
  1376.     return new CIRCChanUser (this, nick, isOp, isVoice);
  1377.     
  1378. }
  1379.  
  1380. CIRCChannel.prototype.getUser =
  1381. function chan_getuser (nick) 
  1382. {
  1383.     
  1384.     nick = nick.toLowerCase(); // assumes valid param!
  1385.     var cuser = this.users[nick];
  1386.     return cuser; // caller expected to check for undefinededness    
  1387.  
  1388. }
  1389.  
  1390. CIRCChannel.prototype.removeUser =
  1391. function chan_removeuser (nick)
  1392. {
  1393.     delete this.users[nick.toLowerCase()]; // see ya
  1394. }
  1395.  
  1396. CIRCChannel.prototype.getUsersLength = 
  1397. function chan_userslen ()
  1398. {
  1399.     var i = 0;
  1400.  
  1401.     for (var p in this.users)
  1402.         i++;
  1403.  
  1404.     return i;
  1405.     
  1406. }
  1407.  
  1408. CIRCChannel.prototype.setTopic = 
  1409. function chan_topic (str)
  1410. {
  1411.     if ((!this.mode.publicTopic) && 
  1412.         (!this.parent.me.isOp))
  1413.         return false;
  1414.     
  1415.     str = String(str).split("\n");
  1416.     for (var i in str)
  1417.         this.parent.sendData ("TOPIC " + this.name + " :" + str[i] + "\n");
  1418.     
  1419.     return true;
  1420.  
  1421. }
  1422.  
  1423. CIRCChannel.prototype.say = 
  1424. function chan_say (msg)
  1425. {
  1426.  
  1427.     this.parent.sayTo (this.name, msg);
  1428.     
  1429. }
  1430.  
  1431. CIRCChannel.prototype.act = 
  1432. function chan_say (msg)
  1433. {
  1434.  
  1435.     this.parent.actTo (this.name, msg);
  1436.     
  1437. }
  1438.  
  1439. CIRCChannel.prototype.notice = 
  1440. function chan_notice (msg)
  1441. {
  1442.  
  1443.     this.parent.noticeTo (this.name, msg);
  1444.     
  1445. }
  1446.  
  1447. CIRCChannel.prototype.ctcp = 
  1448. function chan_ctcpto (code, msg, type)
  1449. {
  1450.     if (typeof msg == "undefined")
  1451.         msg = "";
  1452.  
  1453.     if (typeof type == "undefined")
  1454.         type = "PRIVMSG";
  1455.     
  1456.      
  1457.     this.parent.messageTo (type, this.name, msg, code);
  1458.  
  1459. }
  1460.  
  1461. CIRCChannel.prototype.join = 
  1462. function chan_join (key)
  1463. {
  1464.     if (!key)
  1465.         key = "";
  1466.     
  1467.     this.parent.sendData ("JOIN " + this.name + " " + key + "\n");
  1468.     return true;
  1469.     
  1470. }
  1471.  
  1472. CIRCChannel.prototype.part = 
  1473. function chan_part ()
  1474. {
  1475.     
  1476.     this.parent.sendData ("PART " + this.name + "\n");
  1477.     return true;
  1478.     
  1479. }
  1480.  
  1481. /**
  1482.  * Invites a user to a channel.
  1483.  *
  1484.  * @param nick  the user name to invite.
  1485.  */
  1486. CIRCChannel.prototype.invite =
  1487. function chan_inviteuser (nick)
  1488. {
  1489.  
  1490.     this.parent.sendData("INVITE " + nick + " " + this.name + "\n");
  1491.     return true;
  1492.  
  1493. }
  1494.  
  1495. /*
  1496.  * channel mode
  1497.  */
  1498. function CIRCChanMode (parent)
  1499. {
  1500.  
  1501.     this.parent = parent;
  1502.     this.limit = -1;
  1503.     this.key = "";
  1504.     this.moderated = false;
  1505.     this.publicMessages = true;
  1506.     this.publicTopic = true;
  1507.     this.invite = false;
  1508.     this.pvt = false;
  1509.     
  1510. }
  1511.  
  1512. CIRCChanMode.prototype.TYPE = "IRCChanMode";
  1513.  
  1514. CIRCChanMode.prototype.getModeStr = 
  1515. function chan_modestr (f)
  1516. {
  1517.     var str = "";
  1518.  
  1519.     if (this.invite)
  1520.         str += "i";
  1521.     if (this.moderated)
  1522.         str += "m";
  1523.     if (!this.publicMessages)
  1524.         str += "n";
  1525.     if (!this.publicTopic)
  1526.         str += "t";
  1527.     if (this.pvt)
  1528.         str += "p";
  1529.  
  1530.     if (str)
  1531.         str = "+" + str;
  1532.  
  1533.     return str;
  1534.     
  1535. }
  1536.  
  1537. CIRCChanMode.prototype.setMode = 
  1538. function chanm_mode (modestr)
  1539. {
  1540.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1541.         return false;
  1542.  
  1543.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1544.                                  modestr + "\n");
  1545.  
  1546.     return true;
  1547.     
  1548. }
  1549.  
  1550. CIRCChanMode.prototype.setLimit = 
  1551. function chanm_limit (n)
  1552. {
  1553.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1554.         return false;
  1555.  
  1556.     if ((typeof n == "undefined") || (n <= 0))
  1557.         this.parent.parent.sendData ("MODE " + this.parent.name + " -l\n");
  1558.     else
  1559.         this.parent.parent.sendData ("MODE " + this.parent.name + " +l " +
  1560.                                      Number(n) + "\n");
  1561.  
  1562.     return true;
  1563.     
  1564. }
  1565.  
  1566. CIRCChanMode.prototype.lock = 
  1567. function chanm_lock (k)
  1568. {
  1569.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1570.         return false;
  1571.     
  1572.     this.parent.parent.sendData ("MODE " + this.parent.name + " +k " +
  1573.                                  k + "\n");
  1574.     return true;
  1575.     
  1576. }
  1577.  
  1578. CIRCChanMode.prototype.unlock = 
  1579. function chan_unlock (k)
  1580. {
  1581.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1582.         return false;
  1583.     
  1584.     this.parent.parent.sendData ("MODE " + this.parent.name + " -k " +
  1585.                                  k + "\n");
  1586.     return true;
  1587.     
  1588. }
  1589.  
  1590. CIRCChanMode.prototype.setModerated = 
  1591. function chan_moderate (f)
  1592. {
  1593.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1594.         return false;
  1595.  
  1596.     var modifier = (f) ? "+" : "-";
  1597.     
  1598.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1599.                                  modifier + "m\n");
  1600.     return true;
  1601.     
  1602. }
  1603.  
  1604. CIRCChanMode.prototype.setPublicMessages = 
  1605. function chan_pmessages (f)
  1606. {
  1607.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1608.         return false;
  1609.  
  1610.     var modifier = (f) ? "-" : "+";
  1611.     
  1612.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1613.                                  modifier + "n\n");
  1614.     return true;
  1615.     
  1616. }
  1617.  
  1618. CIRCChanMode.prototype.setPublicTopic = 
  1619. function chan_ptopic (f)
  1620. {
  1621.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1622.         return false;
  1623.  
  1624.     var modifier = (f) ? "-" : "+";
  1625.     
  1626.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1627.                                  modifier + "t\n");
  1628.     return true;
  1629.     
  1630. }
  1631.  
  1632. CIRCChanMode.prototype.setInvite = 
  1633. function chan_invite (f)
  1634. {
  1635.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1636.         return false;
  1637.  
  1638.     var modifier = (f) ? "+" : "-";
  1639.     
  1640.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1641.                                  modifier + "i\n");
  1642.     return true;
  1643.     
  1644. }
  1645.  
  1646. CIRCChanMode.prototype.setPvt = 
  1647. function chan_pvt (f)
  1648. {
  1649.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1650.         return false;
  1651.  
  1652.     var modifier = (f) ? "+" : "-";
  1653.     
  1654.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1655.                                  modifier + "p\n");
  1656.     return true;
  1657.     
  1658. }
  1659.  
  1660. CIRCChanMode.prototype.setSecret = 
  1661. function chan_secret (f)
  1662. {
  1663.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1664.         return false;
  1665.  
  1666.     var modifier = (f) ? "+" : "-";
  1667.     
  1668.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1669.                                  modifier + "p\n");
  1670.     return true;
  1671.     
  1672. }
  1673.  
  1674. /*
  1675.  * user
  1676.  */
  1677.  
  1678. function CIRCUser (parent, nick, name, host)
  1679. {
  1680.     var properNick = nick;
  1681.     nick = nick.toLowerCase();
  1682.     
  1683.     var existingUser = parent.users[nick];
  1684.  
  1685.     if (typeof existingUser == "object")
  1686.     {
  1687.         if (name) existingUser.name = name;
  1688.         if (host) existingUser.host = host;
  1689.         return existingUser;
  1690.     }
  1691.  
  1692.     this.parent = parent;
  1693.     this.nick = nick;
  1694.     this.properNick = properNick;
  1695.     this.name = name;
  1696.     this.host = host;
  1697.  
  1698.     parent.users[nick] = this;
  1699.  
  1700. }
  1701.  
  1702. CIRCUser.prototype.TYPE = "IRCUser";
  1703.  
  1704. CIRCUser.prototype.changeNick =
  1705. function usr_changenick (nick)
  1706. {
  1707.  
  1708.     this.properNick = nick;
  1709.     this.nick = nick.toLowerCase();
  1710.     
  1711. }
  1712.  
  1713. CIRCUser.prototype.getHostMask = 
  1714. function usr_hostmask (pfx)
  1715. {
  1716.     pfx = (typeof pfx != "undefined") ? pfx : "*!" + this.name + "@*.";
  1717.     var idx = this.host.indexOf(".");
  1718.     if (idx == -1)
  1719.         return pfx + this.host;
  1720.     
  1721.     return (pfx + this.host.substr(idx + 1, this.host.length));
  1722.     
  1723. }
  1724.  
  1725. CIRCUser.prototype.say = 
  1726. function usr_say (msg)
  1727. {
  1728.  
  1729.     this.parent.sayTo (this.nick, msg);
  1730.     
  1731. }
  1732.  
  1733. CIRCUser.prototype.notice = 
  1734. function usr_notice (msg)
  1735. {
  1736.  
  1737.     this.parent.noticeTo (this.nick, msg);
  1738.     
  1739. }
  1740.  
  1741. CIRCUser.prototype.act = 
  1742. function usr_act (msg)
  1743. {
  1744.  
  1745.     this.parent.actTo (this.nick, msg);
  1746.     
  1747. }
  1748.  
  1749. CIRCUser.prototype.ctcp = 
  1750. function usr_ctcp (code, msg, type)
  1751. {
  1752.     if (typeof msg == "undefined")
  1753.         msg = "";
  1754.  
  1755.     if (typeof type == "undefined")
  1756.         type = "PRIVMSG";
  1757.     
  1758.      
  1759.     this.parent.messageTo (type, this.name, msg, code);
  1760.  
  1761. }
  1762.  
  1763. CIRCUser.prototype.whois =
  1764. function usr_whois ()
  1765. {
  1766.  
  1767.     this.parent.whois (this.nick);
  1768. }   
  1769.  
  1770.     
  1771. /*
  1772.  * channel user
  1773.  */
  1774. function CIRCChanUser (parent, nick, isOp, isVoice)
  1775. {
  1776.     var properNick = nick;
  1777.     nick = nick.toLowerCase();    
  1778.  
  1779.     var existingUser = parent.users[nick];
  1780.  
  1781.     if (typeof existingUser != "undefined")
  1782.     {
  1783.         if (typeof isOp != "undefined") existingUser.isOp = isOp;
  1784.         if (typeof isVoice != "undefined") existingUser.isVoice = isVoice;
  1785.         return existingUser;
  1786.     }
  1787.         
  1788.     protoUser = new CIRCUser (parent.parent, properNick);
  1789.         
  1790.     this.__proto__ = protoUser;
  1791.     this.setOp = cusr_setop;
  1792.     this.setVoice = cusr_setvoice;
  1793.     this.setBan = cusr_setban;
  1794.     this.kick = cusr_kick;
  1795.     this.kickBan = cusr_kban;
  1796.     this.say = cusr_say;
  1797.     this.notice = cusr_notice;
  1798.     this.act = cusr_act;
  1799.     this.whois = cusr_whois;
  1800.     this.parent = parent;
  1801.     this.isOp = (typeof isOp != "undefined") ? isOp : false;
  1802.     this.isVoice = (typeof isVoice != "undefined") ? isVoice : false;
  1803.     this.TYPE = "IRCChanUser";
  1804.     
  1805.     parent.users[nick] = this;
  1806.  
  1807. }
  1808.  
  1809. function cusr_setop (f)
  1810. {
  1811.     var server = this.parent.parent;
  1812.     var me = server.me;
  1813.  
  1814.     if (!this.parent.users[me.nick].isOp)
  1815.         return false;
  1816.  
  1817.     var modifier = (f) ? " +o " : " -o ";
  1818.     server.sendData("MODE " + this.parent.name + modifier + this.nick + "\n");
  1819.  
  1820.     return true;
  1821.     
  1822. }
  1823.  
  1824. function cusr_setvoice (f)
  1825. {
  1826.     var server = this.parent.parent;
  1827.     var me = server.me;
  1828.  
  1829.     if (!this.parent.users[me.nick].isOp)
  1830.         return false;
  1831.     
  1832.     var modifier = (f) ? " +v " : " -v ";
  1833.     server.sendData("MODE " + this.parent.name + modifier + this.nick + "\n");
  1834.  
  1835.     return true;
  1836.     
  1837. }
  1838.  
  1839. function cusr_kick (reason)
  1840. {
  1841.     var server = this.parent.parent;
  1842.     var me = server.me;
  1843.  
  1844.     reason = (typeof reason != "undefined") ? reason : this.nick;
  1845.     if (!this.parent.users[me.nick].isOp)
  1846.         return false;
  1847.     
  1848.     server.sendData("KICK " + this.parent.name + " " + this.nick + " :" +
  1849.                     reason + "\n");
  1850.  
  1851.     return true;
  1852.     
  1853. }
  1854.  
  1855. function cusr_setban (f)
  1856. {
  1857.     var server = this.parent.parent;
  1858.     var me = server.me;
  1859.  
  1860.     if (!this.parent.users[me.nick].isOp)
  1861.         return false;
  1862.  
  1863.     if (!this.host)
  1864.         return false;
  1865.  
  1866.     var modifier = (f) ? " +b " : " -b ";
  1867.     modifier += this.getHostMask() + " ";
  1868.     
  1869.     server.sendData("MODE " + this.parent.name + modifier + "\n");
  1870.  
  1871.     return true;
  1872.     
  1873. }
  1874.  
  1875. function cusr_kban (reason)
  1876. {
  1877.     var server = this.parent.parent;
  1878.     var me = server.me;
  1879.  
  1880.     if (!this.parent.users[me.nick].isOp)
  1881.         return false;
  1882.  
  1883.     if (!this.host)
  1884.         return false;
  1885.  
  1886.     reason = (typeof reason != "undefined") ? reason : this.nick;
  1887.     var modifier = " -o+b " + this.nick + " " + this.getHostMask() + " ";
  1888.     
  1889.     server.sendData("MODE " + this.parent.name + modifier + "\n" +
  1890.                     "KICK " + this.parent.name + " " + this.nick + " :" +
  1891.                     reason + "\n");
  1892.  
  1893.     return true;
  1894.     
  1895. }
  1896.  
  1897. function cusr_say (msg)
  1898. {
  1899.  
  1900.     this.__proto__.say (msg);
  1901.  
  1902. }
  1903.  
  1904. function cusr_notice (msg)
  1905. {
  1906.  
  1907.     this.__proto__.notice (msg);
  1908.     
  1909. }
  1910.  
  1911. function cusr_act (msg)
  1912. {
  1913.  
  1914.     this.__proto__.act (msg);
  1915.     
  1916. }
  1917.  
  1918. function cusr_whois ()
  1919. {
  1920.  
  1921.     this.__proto__.whois ();
  1922.  
  1923. }
  1924.