home *** CD-ROM | disk | FTP | other *** search
/ Acorn User 10 / AU_CD10.iso / Updates / Perl / Non-RPC / !Perl / scripts / dsirc < prev    next >
Text File  |  1997-10-04  |  63KB  |  2,201 lines

  1. #!/usr/local/bin/perl
  2.  
  3. # dsirc: dumb-mode small irc client in perl
  4. # by orabidoo <roger.espel.llima@pobox.com>
  5. #
  6. # Copyright (C) 1995-1997 Roger Espel Llima
  7. #
  8. # for a full-screen termcap interface, use this with ssfe
  9. #
  10. # use: dsirc [options] [nick [server[:port[:password]]]]
  11. # options are:
  12. #    -p = specify port number
  13. #    -i = specify IRCNAME
  14. #    -n = specify nickname (quite useless as an option)
  15. #    -s = specify server (quite useless as an option)
  16. #    -l = specify file to be loaded instead of ~/.sircrc.pl
  17. #    -L = specify file to be loaded instead of ~/.sircrc
  18. #    -H = specify virtual host to bind to
  19. #    -q = don't load ~/.sircrc or ~/.sircrc.pl
  20. #    -Q = don't load system sircrc or sircrc.pl
  21. #    -R = run in restricted (secure) mode
  22. #    -r = raw mode (no control-char filtering)
  23. #    -8 = 8-bit mode
  24.  
  25. # This program is free software; you can redistribute it and/or modify
  26. # it under the terms of the GNU General Public License as published by
  27. # the Free Software Foundation. See the file LICENSE for more details.
  28. #
  29. # If you make improvements to sirc, please send me the modifications
  30. # (context diffs appreciated) and they might make it to the next release.
  31. #
  32. # For bug reports, comments, questions, email roger.espel.llima@pobox.com
  33. #
  34. # You can always find the latest version of sirc at the following URL:
  35. # http://www.eleves.ens.fr:8080/home/espel/sirc.html
  36.  
  37. $version='2.2';
  38. $date='12 Jun 1997';
  39. $add_ons='';
  40.  
  41. $libdir=$ENV{"SIRCLIB"} || ".";
  42. push(@INC, $libdir, $ENV{"HOME"});
  43. @loadpath=($ENV{"HOME"}."/.sirc", $libdir, ".");
  44.  
  45. $|=1;
  46.  
  47. if (!eval "require 'getopts.pl';") {
  48.   print "\n\n\
  49. Your perl interpreter is *really* screwed up: the getopts.pl library is not
  50. even there! Have you even bothered to run 'install'?\n";
  51.   exit;
  52. }
  53.  
  54. if ($] >= 5 && (eval "use Socket;", $@ eq '')) {
  55. } elsif (-f "$libdir/sircsock.ph") {
  56.   do "$libdir/sircsock.ph";
  57. } elsif (-f $ENV{'HOME'}."/sircsock.ph") {
  58.   do $ENV{'HOME'}."/sircsock.ph";
  59. } elsif (!eval "require 'sys/socket.ph';") {
  60.     print "\n\n\
  61. Your perl installation is wrong somewhere, the sys/socket.ph include file
  62. couldn't be found. Have you even bothered to run 'install'?\n";
  63.     exit;
  64. }
  65.  
  66. &Getopts('n:s:p:u:i:l:L:H:rqQR78');
  67.  
  68. %set=("LOGFILE", "", "LOG", "off", "PRINTUH", "none", "PRINTCHAN", "off",
  69.      "LOCALHOST", "", "CTCP", "noflood", "SENDAHEAD", 4096,
  70.     "USERINFO", "", "FINGER", "", "IRCNAME", "", "EIGHT_BIT", "on");
  71.  
  72. $raw_mode=$opt_r || (!-t STDOUT);
  73. $ansi=!$raw_mode && $ENV{"TERM"} =~ /^vt|^xterm|^ansi/i;
  74. $server=$opt_s || $ARGV[1] || $ENV{"SIRCSERVER"} || $ENV{"IRCSERVER"} ||
  75.                   "irc.primenet.com";
  76. $port0=$opt_p || $ENV{"SIRCPORT"} || $ENV{"IRCPORT"} || 6667;
  77. $username=$opt_u || $ENV{"SIRCUSER"} || $ENV{"IRCUSER"} || (getpwuid($<))[0] ||
  78.                 $ENV{"USER"} || "blah";
  79. $set{"IRCNAME"}=$opt_i || $ENV{"SIRCNAME"} || $ENV{"IRCNAME"} || "sirc user";
  80. $nick=$opt_n || $ARGV[0] || $ENV{"SIRCNICK"} || $ENV{"IRCNICK"} || $username;
  81. $set{"FINGER"}=$ENV{"IRCFINGER"} || "keep your fingers to yourself";
  82. $set{"USERINFO"}=$ENV{"USERINFO"} || "yep, I'm a user";
  83. ($server, $port, $pass)=split(/[\s:]+/, $server);
  84. $port || ($port=$port0);
  85. $server0=$server1=$server;
  86. $initfile=$opt_l || $ENV{"SIRCRCPL"} || $ENV{'HOME'}."/.sircrc.pl" 
  87.   if $opt_l || !$opt_q;
  88. $sysinit=$libdir."/sircrc.pl" if $libdir ne '.' && !$opt_Q;
  89. $rcfile=$opt_L || $ENV{"SIRCRC"} || $ENV{'HOME'}."/.sircrc" 
  90.   if $opt_L || !$opt_q;
  91. $sysrc=$libdir."/sircrc" if $libdir ne '.' && !$opt_Q;
  92. $set{"LOGFILE"}=$logfile=$ENV{'HOME'}."/sirc.log";
  93. $opt_8 || ($set{"EIGHT_BIT"}="off");
  94. $restrict=$opt_R;
  95. $set{"LOCALHOST"}=$opt_H || $ENV{"SIRCHOST"} || $ENV{"IRCHOST"} ||
  96.         $ENV{"LOCALHOST"} || "";
  97.  
  98. if ($set{"LOCALHOST"}) {
  99.   $bindaddr=&resolve($set{"LOCALHOST"});
  100. }
  101.  
  102. @ARGV=();  # ignore any more arguments
  103.  
  104. if (open(H, "$libdir/sirc.help") || ((-f "$libdir/sirc.help.gz") &&
  105.      open(H, "gzip -cd $libdir/sirc.help.gz |"))) {
  106.   @help=<H>;
  107.   close H;
  108.   foreach (@help) {
  109.     chop;
  110.     s/\$version/$version/g;
  111.     s/\$date/$date/g;
  112.   }
  113. } else {
  114.   print "*** Warning: help file ($libdir/sirc.help) not found!\n";
  115. }
  116.  
  117. sub exit {
  118.   close LOG if $logging;
  119.   exit 0;
  120. }
  121.  
  122. $SIG{'PIPE'}='IGNORE';
  123. $SIG{'QUIT'}='IGNORE';
  124. $SIG{'INT'}='exit';
  125.  
  126. sub eq {
  127.   local($a, $b)=@_;
  128.   $a =~ tr/A-Z/a-z/;
  129.   $b =~ tr/A-Z/a-z/;
  130.   return ($a eq $b);
  131. }
  132.  
  133. sub tilde {
  134.   $_[0] =~ s|^\~(\w+)|(getpwnam($1))[7]|e;
  135.   $_[0] =~ s/^\~/$ENV{'HOME'}/;
  136.   $_[0]="." if $_[0] eq '';
  137. }
  138.  
  139. sub sigquit {
  140.   # really ugly hack, but it works...
  141.   close($trysock);
  142. }
  143.  
  144. sub resolve {
  145.   if ($_[0] =~ /^\d+$/) {
  146.     return pack("N", $_[0]+0);
  147.   } elsif ($_[0] =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
  148.     return pack("c4", $1, $2, $3, $4);
  149.   } else {
  150.     return (gethostbyname($_[0]))[4];
  151.   }
  152. }
  153.  
  154. $nextfh="sircblah000";
  155. sub newfh {
  156.   return ++$nextfh;
  157. }
  158.  
  159. sub connect {
  160.   $_[0]=&newfh;
  161.   local($fh, $host, $port)=@_;
  162.   local($adr, $otherend)=&resolve($host);
  163.   &tell("*\cbE\cb* Hostname `$host' not found"), return 0 unless $adr;
  164.   $otherend=pack("S n a4 x8", &AF_INET, $port, $adr);
  165.   &print("*\cbE\cb* Out of file descriptors"), return 0
  166.     unless socket($fh, &PF_INET, &SOCK_STREAM, 0);
  167.   if ($set{"LOCALHOST"}) {
  168.     bind($fh, pack("S n a4 x8", &AF_INET, 0, $bindaddr)) ||
  169.       &tell("*\cbE\cb* Warning: can't bind to sirc host ".$set{'LOCALHOST'});
  170.   }
  171.   $trysock=$fh;
  172.   $SIG{'QUIT'}='sigquit';
  173.   &print("*\cbE\cb* Can't connect to host: $!"), close $fh,
  174.     $SIG{'QUIT'}='IGNORE', return 0 unless connect($fh, $otherend);
  175.   $SIG{'QUIT'}='IGNORE';
  176.   $bindaddr=(unpack("S n a4", getsockname($fh)))[2] if !$bindaddr;
  177.   select($fh); $|=1; select(STDOUT);
  178.   return 1;
  179. }
  180.  
  181. sub listen {
  182.   $_[0]=&newfh;
  183.   local($fh, $port)=@_;
  184.   local($thisend);
  185.   $bindaddr=pack("x4") unless $bindaddr;
  186.   $thisend=pack("S n a4 x8", &AF_INET, $port+0, $bindaddr);
  187.   &tell("*\cbE\cb* Out of file descriptors"), return 0
  188.     unless socket($fh, &PF_INET, &SOCK_STREAM, 0);
  189.   &tell("*\cbE\cb* Can't bind local socket!"), close $fh, return 0
  190.     unless bind($fh, $thisend);
  191.   &tell("*\cbE\cb* Can't listen to socket!"), close $fh, return
  192.     unless listen($fh, 5);
  193.   return (unpack("S n", getsockname($fh)))[1];
  194. }
  195.  
  196. sub accept {
  197.   $_[0]=&newfh;
  198.   return (accept($_[0], $_[1]), close($_[1]))[0];
  199. }
  200.  
  201. sub bindtoserver {
  202.   @channels=(); $talkchannel='';
  203.   %mode=(); $umode=''; %limit=(); %haveops=(); %chankey=(); $away='';
  204.   $listmin=0; $listmax=100000; $listpat='';
  205.   @waituh=(); @douh=(); @erruh=(); $invited='';
  206.   &dostatus;
  207.   &tell("*** Connecting to $server, port $port...");
  208.   &connect($S, $server, $port) || return;
  209.   $connected=1;
  210.   $server1=$server;
  211.   &sl("PASS $pass") if $pass;
  212.   &sl("USER $username blah blah :".$set{'IRCNAME'});
  213.   &sl("NICK $nick");
  214.   @channels=(); $talkchannel=''; %mode=(); $umode=''; %limit=();
  215.   %haveops=(); %chankey=();
  216. }
  217.  
  218. sub gl {
  219.   if ($buffer{$_[0]} =~ /^([^\n\r]*)\r?\n\r?/) {
  220.     $buffer{$_[0]}=$';
  221.     $_=$1."\n";
  222.     return 1;
  223.   }
  224.   local($buf)='';
  225.   if (sysread($_[0], $buf, 4096)) {
  226.     $buffer{$_[0]}.=$buf;
  227.     if ($buffer{$_[0]} =~ /^([^\n\r]*)\r?\n\r?/) {
  228.       $buffer{$_[0]}=$';
  229.       $_=$1."\n";
  230.       return 1;
  231.     }
  232.     return '';
  233.   }
  234.   $_='';
  235.   return 1;
  236. }
  237.  
  238. sub sl {
  239.   &print("*\cbE\cb* Error writing to server: $!") unless print $S $_[0]."\n";
  240. }
  241.  
  242. sub dostatus {
  243.   return unless $ssfe;
  244.   local($t, $s)=($talkchannel, " [sirc]  ");
  245.   $t =~ tr/A-Z/a-z/;
  246.   $s.="*" if $umode =~ /o/;
  247.   $s.="\@" if $t && $haveops{$t};
  248.   $s.=$nick;
  249.   $s.=" (+$umode)" if $umode;
  250.   $s.=" [query: ${query}]" if $query;
  251.   $s.=" (away)" if $away;
  252.   if ($talkchannel ne '') {
  253.     $s.=" on $talkchannel (+$mode{$t})";
  254.     $s.=" <key: $chankey{$t}>" if $chankey{$t};
  255.     $s.=" <limit: $limit{$t}>" if $limit{$t};
  256.   }
  257.   &dohooks("status", $s);
  258.   $laststatus=$s, print "`#ssfe#s$s\n" if $laststatus ne $s;
  259. }
  260.  
  261. $bold="\c[[1m";
  262. $underline="\c[[4m";
  263. $reverse="\c[[7m";
  264. $normal="\c[[m";
  265. $cls="\c[[H\c[[2J";
  266.  
  267. sub enhance {
  268.   local($what)=@_;
  269.   $what =~ tr/\c@-\c^/@-^/;
  270.   return "\cv${what}\cv";
  271. }
  272.  
  273. sub print {
  274.   local($skip, $what)=(0, @_);
  275.   &dohooks("print", $what);
  276.   return if $skip;
  277.   $what =~ s/\s+$//;
  278.   # thanks to Toy (wacren@obspm.fr) for this translation
  279.   $what =~ tr/\x80-\xff/\x00-\x1f !cLxY|$_ca<\-\-R_o+23\'mp.,1o>123?AAAAAAACEEEEIIIIDNOOOOO*0UUUUYPBaaaaaaaceeeeiiiidnooooo:0uuuuypy/
  280.     if $set{"EIGHT_BIT"} ne 'on';
  281.   $logging && print LOG $what."\n";
  282.   if ($raw_mode) {
  283.     print $what, "\n" || &exit;
  284.   } elsif ($ansi) {
  285.     # this is buggy if you combine effects
  286.     $what =~ s/([\ca\cc-\ch\cj-\cu\cw-\c^])/&enhance($1)/eg;
  287.     while ($what =~ /\cb/) {
  288.       ($what =~ s/\cb([^\cb]*)\cb/$bold$1$normal/) ||
  289.       $what =~ s/\cb/$bold/g;
  290.     }
  291.     while ($what =~ /\c_/) {
  292.       ($what =~ s/\c_([^\c_]*)\c_/$underline$1$normal/) ||
  293.       $what =~ s/\c_/$underline/g;
  294.     }
  295.     while ($what =~ /\cv/) {
  296.       ($what =~ s/\cv([^\cv]*)\cv/$reverse$1$normal/) ||
  297.       $what =~ s/\cv/$reverse/g;
  298.     }
  299.     print $what, $normal, "\n" || &exit;
  300.   } else {
  301.     $what =~ tr/\ca-\ch\cj-\c_//d;
  302.     print $what, "\n" || &exit;
  303.   }
  304. }
  305.  
  306. sub tell {
  307.   $silent || &print;
  308. }
  309.  
  310. sub dohooks {
  311.   $hooktype=shift;
  312.   local(@hl);
  313.   eval "\@hl=\@${hooktype}_hooks;";
  314.   foreach $h (@hl) {
  315.     eval { &$h(@_); };
  316.     $@ =~ s/\n$//, &tell("*\cbE\cb* error in $hooktype hook &$h: $@")
  317.     if $@ ne '';
  318.   }
  319. }
  320.  
  321. sub dcerror {
  322.   local($fh, $n)=($_[0], $dcnick{$_[0]});
  323.   &dohooks("chat_disconnect", $n);
  324.   &tell("*\cbE\cb* DCC chat with $n lost");
  325.   close($fh);
  326.   $n =~ tr/A-Z/a-z/;
  327.   delete $dcnick{$fh};
  328.   delete $dcvol{$n};
  329.   delete $dcfh{$n};
  330.   delete $buffer{$fh};
  331. }
  332.  
  333. sub dgsclose {
  334.   local($sfh, $rfh)=@_;
  335.   &dohooks("dcc_disconnect", $dnick{$sfh}, $dfile{$rfh}, $dtransferred{$sfh},
  336.        time-$dstarttime{$rfh});
  337.   &tell("*\cbD\cb* DCC transfer with $dnick{$sfh} terminated; $dtransferred{$sfh} bytes transferred in ".(time-$dstarttime{$rfh}). " seconds");
  338.   close($sfh);
  339.   close($rfh);
  340.   delete $dgrfh{$sfh};
  341.   delete $dsrfh{$sfh};
  342.   delete $dfile{$rfh};
  343.   delete $dstarttime{$rfh};
  344.   delete $dtransferred{$sfh};
  345.   delete $dnick{$sfh};
  346. }
  347.  
  348. sub msg {
  349.   local($towho, $what)=@_;
  350.   print "`#ssfe#t/m $towho \n" if $ssfe && !&eq($towho, $talkchannel);
  351.   if ($towho =~ s/^=//) {
  352.     local($n, $fh)=($towho);
  353.     $n =~ tr/A-Z/a-z/;
  354.     $fh=$dcfh{$n};
  355.     if ($fh) {
  356.       (print $fh $what."\n") || &dcerror($fh);
  357.       $dcvol{$n}+=length($what);
  358.       &dohooks("send_dcc_chat", $towho, $what);
  359.       &tell("|\cb$towho\cb| $what");
  360.     } else {
  361.       &tell("*\cbE\cb* No active DCC chat with $towho");
  362.     }
  363.   } elsif ($connected>1) {
  364.     $what=substr($what, 0, 485);
  365.     &dohooks("send_text", $towho, $what);
  366.     if (&eq($towho, $talkchannel) && !$printchan) {
  367.       &tell("<\c_${nick}\c_> $what");
  368.     } elsif ($towho =~ /^[\&\#\+]/) {
  369.       &tell("<\c_$nick\c_:$towho> $what");
  370.     } else {
  371.       &tell(">\cb${towho}\cb< $what");
  372.     }
  373.     &sl("PRIVMSG $towho :$what");
  374.   } else {
  375.     &tell("*** You're not connected to a server");
  376.   }
  377. }
  378.  
  379. sub say {
  380.   if ($talkchannel) {
  381.     &msg($talkchannel, @_);
  382.   } else {
  383.     &tell("*\cbE\cb* Not on a channel");
  384.   }
  385. }
  386.  
  387. sub notice {
  388.   local($towho, $what)=@_;
  389.   $what=substr($what, 0, 485);
  390.   &dohooks("send_notice", $towho, $what);
  391.   &tell("-> -${towho}- $what");
  392.   &sl("NOTICE $towho :$what");
  393. }
  394.  
  395. sub describe {
  396.   local($towho, $what)=@_;
  397.   $what=substr($what, 0, 480);
  398.   &dohooks("send_action", $towho, $what);
  399.   if (&eq($towho, $talkchannel) && !$printchan) {
  400.     &tell("* $nick $what");
  401.   } elsif ($towho =~ /^[\#\&\+]/) {
  402.     &tell("* ${nick}:${towho} $what");
  403.   } else {
  404.     &tell("*-> \cb${towho}\cb: $nick $what");
  405.   }
  406.   &sl("PRIVMSG $towho :\caACTION".($what eq "" ? "" : " ").$what."\ca");
  407. }
  408.  
  409. sub me {
  410.   if ($talkchannel) {
  411.     &describe($talkchannel, @_);
  412.   } else {
  413.     &tell("*\cbE\cb* Not on a channel");
  414.   }
  415. }
  416.  
  417. sub yetonearg {
  418.   ($newarg, $args)=split(/ +/, $args, 2);
  419.   $args =~ s/^://;
  420. }
  421.  
  422. sub getarg {
  423.   ($newarg, $args)=split(/ +/, $args, 2);
  424. }
  425.  
  426. @weekdays=("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
  427. @months=("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
  428.   "Nov", "Dec");
  429.  
  430. sub date {
  431.   local($sec, $min, $hour, $mday, $mon, $year, $wday)=localtime($_[0]);
  432.   return sprintf("$weekdays[$wday] $months[$mon]  $mday %.2d:%.2d:%.2d %d",
  433.          $hour, $min, $sec, $year+1900);
  434. }
  435.  
  436. sub reply {
  437.   return if $set{"CTCP"} eq 'noreply';
  438.   if ($lastrep<time-10) {
  439.     $lastrep=time;
  440.     $nreps=1;
  441.   } else {
  442.     return if $nreps++>=2 && $set{"CTCP"} eq 'noflood';
  443.   }
  444.   &sl("NOTICE $who :\ca$_[0]\ca");
  445. }
  446.  
  447. sub ctcp {
  448.   local($towho, $to, $what)=$_[0];
  449.   ($what, $args)=split(/ +/, $_[1], 2);
  450.   $what =~ tr/a-z/A-Z/;
  451.   &dohooks("ctcp", $towho, $what, $args);
  452.   return if $skip;
  453.   local($a)=$args;
  454.   $a && ($a=' '.$a);
  455.   $to = (&eq($towho, $nick) ? "you" : $towho);
  456.   &tell("*** $who$puh1 did a CTCP $what$a to $to")
  457.     unless $what =~ /^(ACTION|PING|DCC)$/;
  458.   if ($what eq 'ACTION') {
  459.     &dohooks("action", $towho, $args);
  460.     if (&eq($towho, $nick)) {
  461.       &tell("*> \cb${who}\cb$puh1 $args");
  462.     } elsif (&eq($towho, $talkchannel) && !$printchan) {
  463.       &tell("* $who $args");
  464.     } else {
  465.       &tell("* $who$puh2:$towho $args");
  466.     }
  467.   } elsif ($what eq 'TIME') {
  468.     &reply("TIME ".&date(time));
  469.   } elsif ($what eq 'CLIENTINFO') {
  470.     &reply("CLIENTINFO ACTION, CLIENTINFO, DCC, ECHO, ERRMSG, FINGER, PING, TIME, USERINFO, VERSION");
  471.   } elsif ($what eq 'FINGER') {
  472.     &reply("FINGER ".$set{"FINGER"});
  473.   } elsif ($what eq 'USERINFO') {
  474.     &reply("USERINFO ".$set{"USERINFO"});
  475.   } elsif ($what eq 'VERSION') {
  476.     local($u)=$add_ons;
  477.     $u =~ s/^\+//;
  478.     $u =~ s/\+/ + /g;
  479.     $u=" -- using $u" if $u;
  480.     &reply("VERSION sirc $version, a \cbperl\cb client$u");
  481.   } elsif ($what eq 'PING') {
  482.     &reply("PING $args");
  483.     &tell("*** $who$puh1 did a CTCP PING to $to");
  484.   } elsif ($what eq 'ECHO' || $what eq 'ERRMSG') {
  485.     &reply("$what $args");
  486.   } elsif ($what eq 'DCC') {
  487.     &getarg;
  488.     if ($newarg eq 'CHAT' || $newarg eq 'SEND' && !$restrict) {
  489.       local($dfile, $dhost, $dport, $dsize)=split(/ +/, $args, 4);
  490.       $dfile=$1 if $dfile =~ m|/([^/]*)$|;
  491.       $dfile =~ s/^\./_/;
  492.       if ($dhost==2130706433 || !$dport>1024 || $dhost !~ /^\d+$/ ||
  493.       $dport !~ /^\d+$/) {
  494.     &tell("*\cbE\cb* DCC $newarg ($dfile) from $who$puh1 rejected");
  495.       } elsif ($newarg eq 'CHAT' && grep (&eq($who, $dcwait{$_}),
  496.         keys(%dcwait))) {
  497.     &tell("*\cbD\cb* DCC chat already requested from $who, connecting...");
  498.     local ($wfh)=(grep(&eq($dcwait{$_}, $who), keys(%dcwait)));
  499.     local ($n, $fh)=$who;
  500.     delete $dcwait{$wfh};
  501.     close($wfh);
  502.     &connect($fh, $dhost, $dport) || return;
  503.     $dcnick{$fh}=$who;
  504.     &tell("*\cbD\cb* DCC CHAT with $who established");
  505.     $n =~ tr/A-Z/a-z/;
  506.     $dcvol{$n}=0;
  507.     $dcfh{$n}=$fh;
  508.     print "`#ssfe#t/m =$who \n" if $ssfe;
  509.       } elsif ($newarg eq 'CHAT' && grep(&eq($who, $_), keys(%dcfh))) {
  510.     &tell("*\cbD\cb* DCC chat from $who$puh1 ignored (already established)");
  511.       } else {
  512.     &tell("*\cbD\cb* DCC $newarg ($dfile) from $who$puh1 ".
  513.           ($dsize ? "(size: $dsize) " : "")."[$dhost, $dport]");
  514.     if ($newarg eq 'CHAT') {
  515.       $dcoffered{$who}="$dhost $dport";
  516.       &dohooks("dcc_request", "CHAT", $dhost, $dport);
  517.     } else {
  518.       $dgoffered{"$dhost $dport $dfile"}=$who;
  519.       &dohooks("dcc_request", "SEND", $dhost, $dport, $dfile, $dsize);
  520.     }
  521.       }
  522.     } else {
  523.       &tell("*** $who$puh1 did a CTCP ${what}$a to $to");
  524.     }
  525.   }
  526. }
  527.  
  528. sub doset {
  529.   local($var, $val)=@_;
  530.   $var =~ tr/a-z/A-Z/;
  531.   $val="" unless defined($val);
  532.   if ($var eq 'PRINTUH') {
  533.     $set{$var}="all" if $val =~ /^(on|all)$/i;
  534.     $set{$var}="some" if $val =~ /^some$/i;
  535.     $set{$var}="none" if $val =~ /^(off|none)$/i;
  536.   } elsif ($var eq 'PRINTCHAN') {
  537.     $set{$var}="on", $printchan=1 if $val =~ /^on$/i;
  538.     $set{$var}="off", $printchan=0 if $val =~ /^off$/i;
  539.   } elsif ($var eq 'CTCP') {
  540.     $val =~ tr/A-Z/a-z/;
  541.     $set{$var}=$val if $val =~ /^(none|all)$/;
  542.     $set{$var}="noreply" if $val =~ /^(noreply|off)$/;
  543.     $set{$var}="noflood" if $val =~ /^(noflood|on)$/;
  544.   } elsif ($var eq 'SENDAHEAD') {
  545.     $set{$var}=$val if $val =~ /^\d+$/ && $val<=65536;
  546.   } elsif ($var eq 'USERINFO') {
  547.     $set{$var}=$val;
  548.   } elsif ($var eq 'FINGER') {
  549.     $set{$var}=$val;
  550.   } elsif ($var eq 'IRCNAME') {
  551.     $set{$var}=$val;
  552.   } elsif ($var eq 'EIGHT_BIT') {
  553.     $val =~ tr/A-Z/a-z/;
  554.     $set{$var}=$val if $val =~ /^(on|off)$/;
  555.   } elsif ($var eq 'LOCALHOST') {
  556.     &restrict || return;
  557.     local($ad)=&resolve($val);
  558.     $set{$var}=$val, $bindaddr=$ad if $ad;
  559.   } elsif ($var eq 'LOGFILE') {
  560.     &restrict || return;
  561.     &tilde($val);
  562.     $logfile=$set{$var}=$val;
  563.   } elsif ($var eq 'LOG') {
  564.     &restrict || return;
  565.     if ($val =~ /^on$/i) {
  566.       $logging && close LOG;
  567.       if (open(LOG, 
  568.       ($logfile =~ /\.gz$/ ? "| gzip >> $logfile" : ">> $logfile"))) {
  569.     $logging=1;
  570.     $set{$var}="on";
  571.     select(LOG); $|=1; select(STDOUT);
  572.     print LOG "*\cbL\cb* IRC log started on ".&date(time)."\n";
  573.       } else {
  574.     $logging='';
  575.     $set{$var}="off";
  576.     &tell("*\cbE\cb* Can't write to logfile $logfile");
  577.       }
  578.     } elsif ($val =~ /^off$/i) {
  579.       print LOG "*\cbL\cb* Log ended on ".&date(time)."\n", close LOG
  580.     if $logging;
  581.       $logging='';
  582.       $set{$var}="off";
  583.     }
  584.   } elsif (defined($sets{$var})) {
  585.     local($f)=$sets{$var};
  586.     eval { &$f($val); };
  587.     $@ =~ s/\n$//, &tell("*\cbE\cb* error in SET $var hook: $@") if $@ ne '';
  588.   }
  589. }
  590.  
  591. sub ctcpreply {
  592.   local($ctcp, $rest)=split(/ +/, $_[1], 2);
  593.   $ctcp =~ tr/a-z/A-Z/;
  594.   &dohooks("ctcp_reply", $_[0], $ctcp, $rest);
  595.   $rest=(time-$rest)." seconds" if $ctcp eq 'PING';
  596.   if (&eq($_[0], $nick)) {
  597.     &tell("*** CTCP $ctcp reply from $who$puh1: $rest");
  598.   } else {
  599.     &tell("*** CTCP $ctcp reply to $_[0] from $who$puh2: $rest");
  600.   }
  601. }
  602.  
  603. sub load {
  604.   local($f)=@_;
  605.   &tilde($f);
  606.   if ($f !~ /\//) {
  607.     foreach (@loadpath) {
  608.       $f="$_/$f", last if -f "$_/$f";
  609.       $f="$_/${f}.pl", last if $f !~ /\.pl$/ && -f "$_/${f}.pl";
  610.     }
  611.   } else {
  612.     $f.=".pl" if -f "${f}.pl" && !-f $f;
  613.   }
  614.   if ($f ne '' && -f $f) {
  615.     do $f;
  616.     $@ =~ s/\n$//, &tell("*\cbE\cb* Load error in $f: $@") if $@ ne '';
  617.   } else {
  618.     &tell("*\cbE\cb* $f: File not found");
  619.   }
  620. }
  621.  
  622. sub restrict {
  623.   &tell("*\cbE\cb* Command not available"), return 0 if $restrict;
  624.   1;
  625. }
  626.  
  627. sub dosplat {
  628.   $args =~ s/^\s*\*($|\s)/${talkchannel}${1}/ if $talkchannel;
  629. }
  630.  
  631. sub expand {
  632.   if ($_[0] eq '$') {
  633.     return '$';
  634.   } elsif ($_[0] =~ /^(\d+)$/) {
  635.     return (split(/ +/, $args))[$1];
  636.   } elsif ($_[0] =~ /^(\d+)-$/) {
  637.     return (split(/ +/, $args, 1+$1))[$1];
  638.   } else {
  639.     return eval "\$$_[0]";
  640.   }
  641. }
  642.  
  643. $recdepth=0;
  644. $maxrecursion=20;
  645.  
  646. sub docommand {
  647.   local($line)=@_;
  648.   local($recdepth)=$recdepth+1;
  649.   &print("*\cbE\cb* Max recursion exceeded!"), return
  650.     if $recdepth > $maxrecursion;
  651.   local($noalias)=($line =~ s/^\///);
  652.   local($silent)=1 if $line =~ s/^\^//;
  653.   local($cmd, $args)=split(/ +/, $line, 2);
  654.   $cmd =~ tr/a-z/A-Z/;
  655.   if (!$noalias && defined($aliases{$cmd})) {
  656.     $line=$aliases{$cmd};
  657.     $line.=($args ne '' ? " ".$args : "")
  658.       unless ($line =~ s/\$(\$|\d+-?|\w+)/&expand($1)/eg);
  659.     $line =~ s/^\///;
  660.     $noalias=1 if $line =~ s/^\///;
  661.     $silent=1 if $line =~ s/^\^//;
  662.     ($cmd, $args)=split(/ +/, $line, 2);
  663.     $cmd =~ tr/a-z/A-Z/;
  664.   }
  665.   if (!$noalias && defined($cmds{$cmd})) {
  666.     eval $cmds{$cmd};
  667.     $@ =~ s/\n$//, &tell("*\cbE\cb* error in command $cmd: $@") if $@ ne '';
  668.   } elsif ($cmd eq 'ALIAS') {
  669.     &getarg;
  670.     if ($newarg =~ /^-/) {
  671.       local($a)=$';
  672.       if ($a eq '') {
  673.     %aliases=();
  674.     &tell("*** All aliases removed");
  675.       } else {
  676.     $a =~ tr/a-z/A-Z/;
  677.     delete $aliases{$a};
  678.     &tell("*** Alias $a removed");
  679.       }
  680.     } elsif ($newarg ne '') {
  681.       $newarg =~ tr/a-z/A-Z/;
  682.       if ($args ne '') {
  683.     $aliases{$newarg}=$args;
  684.     &tell("*** $newarg aliased to $args");
  685.       } else {
  686.     if (defined($aliases{$newarg})) {
  687.       &tell("*** $newarg is aliased to: $aliases{$newarg}");
  688.     } else {
  689.       &tell("*** $newarg: no such alias");
  690.     }
  691.       }
  692.     } else {
  693.       foreach $a (sort(keys(%aliases))) {
  694.     &tell("*** $a is aliased to $aliases{$a}");
  695.       }
  696.     }
  697.   } elsif ($cmd eq 'SET') {
  698.     &getarg;
  699.     local($s)=$newarg;
  700.     $s =~ tr/a-z/A-Z/;
  701.     if ($s =~ s/^-//) {
  702.       &tell("*** No such variable $s"), return unless defined($set{$s});
  703.       &doset($s, "");
  704.       &tell("*** $s is ".($set{$s} ne '' ? "set to $set{$s}" : "unset"));
  705.     } elsif ($s ne '') {
  706.       &tell("*** No such variable $s"), return unless defined($set{$s});
  707.       &doset($s, $args) if $args ne '';
  708.       &tell("*** $s is ".($set{$s} ne '' ? "set to $set{$s}" : "unset"));
  709.     } else {
  710.       foreach $s (sort(keys (%set))) {
  711.     &tell("*** $s is ".($set{$s} ne '' ? "set to $set{$s}" : "unset"));
  712.       }
  713.     }
  714.   } elsif ($cmd eq 'NOTIFY' || $cmd eq 'N') {
  715.     if ($args eq '-') {
  716.       &tell("*** Notify list cleared");
  717.       %notify=();
  718.     } elsif ($args eq '') {
  719.       local($l)='';
  720.       foreach (grep($notify{$_}, keys %notify)) {
  721.     &tell("*** Currently present: $l"), $l='' if length($l)>450;
  722.     $l.=$_." ";
  723.       }
  724.       $l && &tell("*** Currently present: $l");
  725.       $l='';
  726.       foreach (grep(!$notify{$_}, keys %notify)) {
  727.     &tell("*** Currently absent: $l"), $l='' if length($l)>450;
  728.     $l.=$_." ";
  729.       }
  730.       $l && &tell("*** Currently absent: $l");
  731.     } else {
  732.       local($w, $n);
  733.       foreach $w (split(/ +/, $args)) {
  734.     if ($w =~ s/^-//) {
  735.       ($n)=(grep(&eq($_, $w), keys(%notify)), '');
  736.       $n ne '' && delete $notify{$n};
  737.       &tell("*** $w removed from notify list");
  738.     } else {
  739.       $notify{$w}='0';
  740.       &tell("*** $w added to notify list");
  741.       $newisons=1;
  742.     }
  743.       }
  744.     }
  745.   } elsif ($cmd eq 'IGNORE' || $cmd eq 'IG') {
  746.     &getarg;
  747.     if ($newarg eq '-') {
  748.       @ignore=();
  749.       &tell("*** Ignore list cleared");
  750.     } elsif ($newarg eq '') {
  751.       local($p);
  752.       &tell("*** You're ignoring:");
  753.       foreach (@ignore) {
  754.     $p=$_;
  755.     $p =~ s/\\//g;
  756.     $p =~ s/\.\*/*/g;
  757.     &tell("*** $p");
  758.       }
  759.     } else {
  760.       local($d, $p)=('');
  761.       $d=1 if $newarg =~ s/^-//;
  762.       if ($newarg =~ /\!.*\@/) {
  763.       } elsif ($newarg !~ /[\@\!]/) {
  764.     $newarg.="!*";
  765.       } elsif ($newarg =~ /\@/) {
  766.     $newarg="*!".$newarg;
  767.       } else {
  768.     $newarg.="\@*";
  769.       }
  770.       $p=$newarg;
  771.       $newarg =~ s/([^\\])\./$1\\./g;
  772.       $newarg =~ s/\*/\.\*/g;
  773.       $newarg =~ s/([^\.\*\\\w])/\\$1/g;
  774.       if ($d) {
  775.         &tell("*** Removing $p from the ignore list");
  776.     @ignore=grep(!&eq($_, $newarg), @ignore);
  777.       } else {
  778.         &tell("*** Ignoring $p ... what a relief!");
  779.     push(@ignore, $newarg);
  780.       }
  781.     }
  782.   } elsif ($cmd eq 'ECHO') {
  783.     &print($args);
  784.   } elsif ($cmd eq 'CLEAR' || $cmd eq 'CL') {
  785.     print $cls if $ansi;
  786.     print "`#ssfe#l\n" if $ssfe;
  787.   } elsif ($cmd eq 'EVAL') {
  788.     &restrict || return;
  789.     eval ($args);
  790.     $@ =~ s/\n$//, &tell("*\cbE\cb* eval error: $@") if $@ ne '';
  791.   } elsif ($cmd eq 'HELP') {
  792.     &tell("*\cbH\cb* Help not available"), return unless @help;
  793.     $args='main' if $args =~ /^\s*$/;
  794.     local($found)='';
  795.     foreach (@help) {
  796.       if (/^\@/) {
  797.     last if $found;
  798.     if (&eq($_, "\@$args")) {
  799.       $found=1;
  800.       &tell("*\cbH\cb* Help on $args") if $args ne 'main';
  801.     }
  802.       } else {
  803.     &tell("*\cbH\cb* $_") if $found;
  804.       }
  805.     }
  806.     &tell("*\cbH\cb* Unknown help topic; try /help") unless $found;
  807.   } elsif ($cmd eq 'LOAD') {
  808.     &restrict || return;
  809.     &getarg;
  810.     &tell("*\cbE\cb* Yeah, but what?"), return if $newarg eq '';
  811.     &load($newarg);
  812.   } elsif ($cmd eq 'VERSION') {
  813.     &tell("*** \cbsirc\cb version $version, written in \cbperl\cb by \cborabidoo\cb");
  814.     $_=$add_ons;
  815.     s/^\+//;
  816.     s/\+/, /g;
  817.     &tell("*** add-ons: $_") if $_;
  818.     $connected==2 && &sl("VERSION $args");
  819.   } elsif ($cmd eq 'CD') {
  820.     &restrict || return;
  821.     &getarg;
  822.     if ($newarg ne '') {
  823.       &tilde($newarg);
  824.       chdir($newarg) || &tell("*\cbE\cb* Can't chdir to $newarg");
  825.     }
  826.     local($cwd); chop($cwd=`pwd`);
  827.     &tell("*** Current directory is $cwd");
  828.   } elsif ($cmd eq 'SYSTEM') {
  829.     &restrict || return;
  830.     system($args);
  831.   } elsif ($cmd eq 'BYE' || $cmd eq 'QUIT' || $cmd eq 'EXIT' ||
  832.         $cmd eq 'SIGNOFF') {
  833.     $args || ($args="using \cbsirc\cb version $version$add_ons");
  834.     &sl("QUIT :$args") if $connected;
  835.     &exit;
  836.   } elsif ($cmd eq 'SERVER') {
  837.     $args=$1 if $args =~ /^\s*(.*)\s*$/;
  838.     $args=$server0 if $args eq '0';
  839.     $args=$server1 if $args eq '1';
  840.     if ($args eq '') {
  841.       &tell($connected ? "*** Your current server is $server" :
  842.              "*** You're not connected to a server");
  843.     } else {
  844.       ($server, $port, $pass)=split(/[\s:]+/, $args);
  845.       $server=$', $nick=$1 if $server =~ /^([^\@]+)\@/;
  846.       $port || ($port=$port0);
  847.       &sl("QUIT :changing servers"), close $S, delete $buffer{$S} if $connected;
  848.       $connected=0;
  849.       &bindtoserver;
  850.     }
  851.   } elsif ($cmd eq 'MSG' || $cmd eq 'M') {
  852.     &dosplat;
  853.     if ($args) {
  854.       ($newarg, $args)=split(/ /, $args, 2);
  855.       &msg($newarg, $args);
  856.     } else {
  857.       &tell("*\cbE\cb* You must specify a nick or channel!");
  858.     }
  859.   } elsif ($cmd eq 'QUERY' || $cmd eq 'Q') {
  860.     if ($args) {
  861.       $args =~ s/\s+$//;
  862.       $query=$args;
  863.       &tell("*** Starting conversation with $query");
  864.       &dostatus;
  865.     } elsif ($query) {
  866.       &tell("*** Ending conversation with $query");
  867.       $query='';
  868.       &dostatus;
  869.     } else {
  870.       &tell("*** You aren't querying anyone :p");
  871.     }
  872.   } elsif ($cmd eq 'DCC') {
  873.     &getarg;
  874.     if ($newarg =~ /^chat$/i) {
  875.       &getarg;
  876.       local($n)=grep(&eq($newarg, $_), keys(%dcoffered));
  877.       if ($n) {
  878.     local($dcadr, $dcport)=split(/ +/, $dcoffered{$n});
  879.     local($fh);
  880.     delete $dcoffered{$n};
  881.     &connect($fh, $dcadr, $dcport) || return;
  882.     $dcnick{$fh}=$n;
  883.     &tell("*\cbD\cb* DCC CHAT with $n established");
  884.     print "`#ssfe#t/m =$n \n" if $ssfe;
  885.     $n =~ tr/A-Z/a-z/;
  886.     $dcvol{$n}=0;
  887.     $dcfh{$n}=$fh;
  888.       } elsif (grep (&eq($newarg, $dcwait{$_}), keys(%dcwait))) {
  889.     &tell("*\cbE\cb* DCC CHAT request to $newarg already sent");
  890.       } elsif (grep(&eq($newarg, $dcnick{$_}), keys(%dcnick))) {
  891.     &tell("*\cbE\cb* DCC CHAT with $newarg already established");
  892.       } elsif ($newarg) {
  893.     &tell("*** You're not connected to a server"), return if $connected<2;
  894.     &tell("*** Don't be antisocial!"), return if &eq($newarg, $nick);
  895.     local($mynumber, $myport, $fh)=unpack("N", $bindaddr);
  896.     $myport=&listen($fh) || return;
  897.     $dcwait{$fh}=$newarg;
  898.     &sl("PRIVMSG $newarg :\caDCC CHAT chat $mynumber $myport\ca");
  899.     &dohooks("send_ctcp", $newarg, "DCC CHAT chat $mynumber $myport");
  900.     &tell("*\cbD\cb* Sent DCC CHAT request to $newarg");
  901.       } else {
  902.     &tell("*** I need a nick");
  903.       }
  904.     } elsif ($newarg =~ /^rchat$/i) {
  905.       &getarg;
  906.       local($n)=$newarg;
  907.       &getarg;
  908.       if ($newarg) {
  909.     local($fh)=grep(&eq($dcnick{$_}, $n), keys(%dcnick));
  910.     &tell("*\cbE\cb* No DCC CHAT established with $n"), return 
  911.       unless $fh;
  912.     &tell("*\cbE\cb* DCC CHAT already established with $newarg"), return 
  913.       if grep(&eq($dcnick{$_}, $newarg), keys(%dcnick));
  914.     &tell("*\cbD\cb* DCC CHAT with $n renamed to $newarg");
  915.     $dcnick{$fh}=$newarg;
  916.     $n =~ tr/A-Z/a-z/;
  917.     $newarg =~ tr/A-Z/a-z/;
  918.     $dcfh{$newarg}=$dcfh{$n};
  919.     $dcvol{$newarg}=$dcvol{$n};
  920.     delete $dcfh{$n};
  921.     delete $dcvol{$n};
  922.       } else {
  923.     &tell("*** I need *two* nicks");
  924.       }
  925.     } elsif ($newarg =~ /^close$/i) {
  926.       &getarg;
  927.       if ($newarg =~ /^chat$/i) {
  928.     &getarg;
  929.     local($n)=$newarg;
  930.     $newarg =~ tr/A-Z/a-z/;
  931.     local($fh)=$dcfh{$newarg};
  932.     local($nn)=(grep(&eq($_, $newarg), keys(%dcoffered)));
  933.     if ($nn) {
  934.       &tell("*\cbD\cb* Forgetting offered DCC CHAT from $nn");
  935.       delete $dcoffered{$nn};
  936.     } elsif ($fh) {
  937.       &dohooks("chat_disconnect", $n);
  938.       &tell("*\cbD\cb* Closing DCC CHAT connection with $n");
  939.       close($fh);
  940.       delete $dcnick{$fh};
  941.       delete $dcvol{$newarg};
  942.       delete $dcfh{$newarg};
  943.       delete $buffer{$fh};
  944.     } elsif (($fh)=grep(&eq($dcwait{$_}, $n), keys (%dcwait)), $fh) {
  945.       close($fh);
  946.       delete $dcwait{$fh};
  947.       &tell("*\cbD\cb* Closing listening DCC CHAT with $n");
  948.     } else {
  949.       $n && &tell("*\cbE\cb* No DCC CHAT connection with $n");
  950.     }
  951.       } elsif ($newarg =~ /^get$/i) {
  952.     &getarg;
  953.     local($found)='';
  954.     foreach $i (keys(%dgoffered)) {
  955.       if (&eq($dgoffered{$i}, $newarg) && (!$args ||
  956.           &eq($args, (split(/ +/, $i))[2]))) {
  957.         &tell("*\cbE\cb* Forgetting pending DCC GET from $newarg");
  958.         delete $dgoffered{$i};
  959.         $found=1;
  960.       }
  961.     }
  962.     foreach $sfh (grep(&eq($newarg, $dnick{$_}), keys(%dnick))) {
  963.       if (!$found && $dgrfh{$sfh}) {
  964.         local($fh)=$dgrfh{$sfh};
  965.         next if $args && ($args ne $dfile{$fh});
  966.         &dohooks("dcc_disconnect", $dnick{$sfh}, $dfile{$fh}, 
  967.         $dtransferred{$sfh}, time-$dstarttime{$fh});
  968.         &tell("*\cbE\cb* Closing DCC GET connection with $newarg");
  969.         $found=1;
  970.         close $sfh;
  971.         close $fh;
  972.         delete $dgrfh{$sfh};
  973.         delete $dfile{$fh};
  974.         delete $dstarttime{$fh};
  975.         delete $dtransferred{$sfh};
  976.         delete $dnick{$sfh};
  977.       }
  978.     }
  979.     &tell("*\cbE\cb* No DCC GET connection with $newarg") unless $found;
  980.       } elsif ($newarg =~ /^send$/i) {
  981.         &getarg;
  982.     local($n, $found, $fh)=($newarg, '');
  983.     &getarg;
  984.     $newarg =~ s/(\W)/\\$1/g;
  985.     foreach $sfh (keys(%dswait), keys(%dsrfh)) {
  986.       next unless &eq($dnick{$sfh}, $n);
  987.       $fh=$dswait{$sfh} || $dsrfh{$sfh} || next;
  988.       if ($newarg eq '' || $dfile{$fh} =~ /^${newarg}$/ ||
  989.           $dfile{$fh} =~ /\/${newarg}$/) {
  990.         &tell("*\cbD\cb* DCC SEND connection with $n closed");
  991.         &dohooks("dcc_disconnect", $dnick{$sfh}, $dfile{$fh}, 
  992.         $dtransferred{$sfh}, time-$dstarttime{$fh});
  993.         close($sfh);
  994.         close($fh);
  995.         delete $dswait{$sfh};
  996.         delete $dsrfh{$sfh};
  997.         delete $dfile{$fh};
  998.         delete $dstarttime{$fh};
  999.         delete $dtransferred{$sfh};
  1000.         delete $dnick{$sfh};
  1001.         $found=1;
  1002.       }
  1003.     }
  1004.     &tell("*\cbE\cb* No DCC SEND connection with $n") unless $found;
  1005.       } else {
  1006.     &tell("*\cbE\cb* Unknown DCC type");
  1007.       }
  1008.     } elsif ($newarg =~ /^rename$/i) {
  1009.       local($found, $n);
  1010.       &getarg;
  1011.       $n=$newarg;
  1012.       &getarg;
  1013.       $args=$newarg, $newarg='' if $args eq '';
  1014.       &tell("*\cbE\cb* I need a filename :p"), return if $args eq '';
  1015.       &tilde($args);
  1016.       foreach $i (keys(%dgoffered)) {
  1017.     if (&eq($dgoffered{$i}, $n) && (!$newarg ||
  1018.         &eq($newarg, (split(/ +/, $i))[2]))) {
  1019.       local($m, $p, $f)=split(/ +/, $i);
  1020.       delete $dgoffered{$i};
  1021.       $dgoffered{"$m $p $args"}=$n;
  1022.       &tell("*\cbD\cb* Renaming \"$f\" (offered by $n) to \"$args\"");
  1023.       $found=1;
  1024.       last;
  1025.     }
  1026.       }
  1027.       &tell("*\cbE\cb* No such file offered by $n") unless $found;
  1028.     } elsif ($newarg =~ /^get$/i) {
  1029.       &getarg;
  1030.       local($n)=grep((&eq($newarg, $dgoffered{$_}) && (!$args ||
  1031.               &eq($args, (split(/ +/, $_))[2]))),
  1032.              keys(%dgoffered));
  1033.       if ($n) {
  1034.     local($dgadr, $dgport, $file)=split(/ +/, $n);
  1035.     local($fh, $sfh);
  1036.     $n=(delete $dgoffered{$n});
  1037.     $fh=&newfh;
  1038.     &print("*\cbE\cb* Can't write to file $file"), return
  1039.       unless open($fh, "> $file");
  1040.     &connect($sfh, $dgadr, $dgport) || return;
  1041.     $dgrfh{$sfh}=$fh;
  1042.     $dnick{$sfh}=$n;
  1043.     $dfile{$fh}=$file;
  1044.     $dstarttime{$fh}=time;
  1045.     $dtransferred{$sfh}=0;
  1046.     &tell("*\cbD\cb* DCC GET connection with $n established");
  1047.       } else {
  1048.     if ($newarg) {
  1049.       &tell("*\cbE\cb* No pending DCC GET from $newarg");
  1050.     } else {
  1051.       &tell("*\cbE\cb* Uhm, who from?");
  1052.     }
  1053.       }
  1054.     } elsif ($newarg =~ /^list$/i || $newarg eq '') {
  1055.       &tell("*\cbD\cb* List of DCC connections:");
  1056.       foreach $n (keys(%dcfh)) {
  1057.     &tell("*\cbD\cb* Established DCC CHAT with $n ($dcvol{$n} bytes)");
  1058.       }
  1059.       foreach $n (keys(%dcoffered)) {
  1060.     &tell("*\cbD\cb* DCC CHAT offered by $n");
  1061.       }
  1062.       foreach $f (keys(%dcwait)) {
  1063.     &tell("*\cbD\cb* DCC CHAT offered to $dcwait{$f}");
  1064.       }
  1065.       foreach $i (keys(%dgoffered)) {
  1066.     &tell("*\cbD\cb* DCC GET $i offered by $dgoffered{$i}");
  1067.       }
  1068.       foreach $s (keys(%dgrfh)) {
  1069.         local($f)=$dgrfh{$s};
  1070.     &tell("*\cbD\cb* DCC GET \"$dfile{$f}\" established with $dnick{$s}, $dtransferred{$s} bytes read in ".(time-$dstarttime{$f})." seconds.");
  1071.       }
  1072.       foreach $s (keys(%dswait)) {
  1073.         local($f)=$dswait{$s};
  1074.     &tell("*\cbD\cb* DCC SEND \"$dfile{$f}\" offered to $dnick{$s}");
  1075.       }
  1076.       foreach $s (keys(%dsrfh)) {
  1077.         local($f)=$dsrfh{$s};
  1078.     &tell("*\cbD\cb* DCC SEND \"$dfile{$f}\" established with $dnick{$s}, $dtransferred{$s} bytes sent in ".(time-$dstarttime{$f})." seconds.");
  1079.       }
  1080.     } elsif ($newarg =~ /^send$/i) {
  1081.       &tell("*** You're not connected to a server"), return if $connected<2;
  1082.       &restrict || return;
  1083.       local($n, $f)=split(/ +/, $args);
  1084.       local($tf, $mynumber, $sz, $fh, $myport, $lfh)=($f, unpack("N", $bindaddr));
  1085.       $fh=&newfh;
  1086.       &tilde($f);
  1087.       &tell("*\cbE\cb* Can't open file $f"), return unless open($fh, "<$f");
  1088.       $myport=&listen($lfh) || (close $fh, return);
  1089.       $dswait{$lfh}=$fh;
  1090.       $tf=$1 if $dfile =~ m|/([^/]*)$|;
  1091.       $sz=(-s $f);
  1092.       &sl("PRIVMSG $n :\caDCC SEND $tf $mynumber $myport $sz\ca");
  1093.       &dohooks("send_ctcp", $n, "DCC SEND $tf $mynumber $myport $sz");
  1094.       &tell("*\cbD\cb* Sent DCC SEND request to $n");
  1095.       $dfile{$fh}=$f;
  1096.       $dswait{$lfh}=$fh;
  1097.       $dnick{$lfh}=$n;
  1098.     } else {
  1099.       &tell("*** I can \"only\" do DCC CHAT, RCHAT, GET, SEND, CLOSE, RENAME and LIST, *sheesh*");
  1100.     }
  1101.   } elsif ($connected<2) {
  1102.     &tell("*** You're not connected to a server");
  1103.   } elsif ($cmd eq 'AWAY') {
  1104.       &sl($args ? "AWAY :$args" : "AWAY");
  1105.   } elsif ($cmd eq 'NEXT') {
  1106.     if ($#channels>0) {
  1107.       $talkchannel=shift(@channels);
  1108.       push(@channels, $talkchannel);
  1109.       !$ssfe && &tell("*** Talking to $talkchannel now");
  1110.       &dostatus;
  1111.     }
  1112.   } elsif ($cmd eq 'SAY' || $cmd eq '') {
  1113.     &say($args);
  1114.   } elsif ($cmd eq 'NOTICE' || $cmd eq 'NO') {
  1115.     &dosplat;
  1116.     if ($args) {
  1117.       ($newarg, $args)=split(/ /, $args, 2);
  1118.       ¬ice($newarg, $args);
  1119.     } else {
  1120.       &tell("*\cbE\cb* You must specify a nick or channel!");
  1121.     }
  1122.   } elsif ($cmd eq 'DESCRIBE' || $cmd eq 'DE') {
  1123.     &dosplat;
  1124.     if ($args) {
  1125.       ($newarg, $args)=split(/ /, $args, 2);
  1126.       &describe($newarg, $args);
  1127.     } else {
  1128.       &tell("*\cbE\cb* You must specify a nick or channel!");
  1129.     }
  1130.   } elsif ($cmd eq 'KICK' || $cmd eq 'K') {
  1131.     &dosplat;
  1132.     &getarg;
  1133.     local($c)=$talkchannel;
  1134.     if ($newarg =~ /^[\#\&\+]/) {
  1135.       $c=$newarg;
  1136.       &getarg;
  1137.     }
  1138.     if ($newarg) {
  1139.       $args || ($args=$nick);
  1140.       &sl("KICK $c $newarg :$args");
  1141.     } else {
  1142.       &tell("*\cbE\cb* You must specify a nick!");
  1143.     }
  1144.   } elsif ($cmd eq 'DISCONNECT' || $cmd eq 'DIS') {
  1145.     &tell("*** Disconnecting from $server");
  1146.     close($S);
  1147.     delete $buffer{$S};
  1148.     $connected=0;
  1149.     &dohooks("disconnect");
  1150.   } elsif ($cmd eq 'INVITE' || $cmd eq 'INV' || $cmd eq 'I') {
  1151.     local(@ns)=split(/ +/, $args);
  1152.     local($l, $c)=(pop(@ns), $talkchannel);
  1153.     if ($l =~ /^[\#\&\+]/) {
  1154.       $c=$l;
  1155.     } else {
  1156.       $l && push(@ns, $l);
  1157.     }
  1158.     foreach (@ns) {
  1159.       &sl("INVITE $_ $c");
  1160.     }
  1161.   } elsif ($cmd eq 'CTCP') {
  1162.     &dosplat;
  1163.     if ($args) {
  1164.       &getarg;
  1165.       local($towho)=$newarg;
  1166.       &getarg;
  1167.       $newarg =~ tr/a-z/A-Z/;
  1168.       $args=" ".$args if $args ne '';
  1169.       &sl("PRIVMSG $towho :\ca$newarg$args\ca");
  1170.       &dohooks("send_ctcp", $towho, $newarg.$args);
  1171.       &tell("*** Sending a CTCP $newarg$args to $towho");
  1172.     } else {
  1173.       &tell("*\cbE\cb* You must specify a nick or channel!");
  1174.     }
  1175.   } elsif ($cmd eq 'PING' || $cmd eq 'P') {
  1176.     &dosplat;
  1177.     if ($args) {
  1178.       &getarg;
  1179.       local($t)=time;
  1180.       &sl("PRIVMSG $newarg :\caPING $t\ca");
  1181.       &dohooks("send_ctcp", $newarg, "PING $t");
  1182.       &tell("*** Sending a CTCP PING to $newarg");
  1183.     } else {
  1184.       &tell("*\cbE\cb* You must specify a nick or channel!");
  1185.     }
  1186.   } elsif ($cmd eq 'ME') {
  1187.     if ($talkchannel) {
  1188.       &describe($talkchannel, $args);
  1189.     } else {
  1190.       &tell("*\cbE\cb* Not on a channel");
  1191.     }
  1192.   } elsif ($cmd eq 'TOPIC' || $cmd eq 'T') {
  1193.     &dosplat;
  1194.     local($c)=$talkchannel;
  1195.     if ($args =~ /^[\#\&\+]/) {
  1196.       &getarg;
  1197.       $c=$newarg;
  1198.     }
  1199.     if ($args) {
  1200.       &sl("TOPIC $c :$args");
  1201.     } else {
  1202.       &sl("TOPIC $c");
  1203.     }
  1204.   } elsif ($cmd eq 'LEAVE' || $cmd eq 'PART' || $cmd eq 'HOP') {
  1205.     &dosplat;
  1206.     $args=$talkchannel if $args eq '';
  1207.     &sl("PART $args");
  1208.   } elsif ($cmd eq 'LL') {
  1209.     if ($talkchannel) {
  1210.       &sl("WHO $talkchannel");
  1211.     } else {
  1212.       &tell("*\cbE\cb* Not on a channel");
  1213.     }
  1214.   } elsif ($cmd eq 'O' || $cmd eq 'OP') {
  1215.     local($c, $n, $l)=($talkchannel, 0, '');
  1216.     &getarg, $c=$newarg if ($args =~ /^[\#\&\+]/);
  1217.     local(@ppl)=split(/ +/, $args);
  1218.     foreach (@ppl) {
  1219.       if ($n<4) {
  1220.     $l .= " ".$_;
  1221.     $n++;
  1222.       } else {
  1223.     &sl("MODE $c +oooo $l");
  1224.     $l=$_;
  1225.     $n=1;
  1226.       }
  1227.     }
  1228.     $l && &sl("MODE $c +oooo $l");
  1229.   } elsif ($cmd eq 'D' || $cmd eq 'DEOP') {
  1230.     local($c, $n, $l)=($talkchannel, 0, '');
  1231.     &getarg, $c=$newarg if ($args =~ /^[\#\&\+]/);
  1232.     local(@ppl)=split(/ +/, $args);
  1233.     foreach (@ppl) {
  1234.       if ($n<4) {
  1235.     $l .= " ".$_;
  1236.     $n++;
  1237.       } else {
  1238.     &sl("MODE $c -oooo $l");
  1239.     $l=$_;
  1240.     $n=1;
  1241.       }
  1242.     }
  1243.     $l && &sl("MODE $c -oooo $l");
  1244.   } elsif ($cmd eq 'W' || $cmd eq 'WHOIS') {
  1245.     &sl($args eq '' ? "WHOIS $nick" : "WHOIS $args");
  1246.   } elsif ($cmd eq 'WI') {
  1247.     &getarg;
  1248.     $newarg=$nick if $newarg eq '';
  1249.     &sl("WHOIS $newarg $newarg");
  1250.   } elsif ($cmd eq 'WHO') {
  1251.     &dosplat;
  1252.     if ($args =~ /^[\s\*]*$/) {
  1253.       &tell("*** Uhm, better not");
  1254.     } else {
  1255.       &sl("WHO $args");
  1256.     }
  1257.   } elsif ($cmd eq 'JOIN' || $cmd eq 'J') {
  1258.     $args=$invited if $args eq '';
  1259.     unless ($args =~ /^[\#\&\+]/) {
  1260.       $args='#'.$args;
  1261.     }
  1262.     if (grep(&eq($_, $args), @channels)) {
  1263.       &tell("*** Talking to $args now");
  1264.       $talkchannel=$args;
  1265.       &dostatus;
  1266.     } else {
  1267.       &sl("JOIN $args");
  1268.     }
  1269.   } elsif ($cmd eq 'QUOTE') {
  1270.     $args ne '' && &sl($args);
  1271.   } elsif ($cmd eq 'UMODE') {
  1272.     &sl("MODE $nick $args");
  1273.   } elsif ($cmd eq 'MO') {
  1274.     if ($talkchannel) {
  1275.       &sl("MODE $talkchannel $args");
  1276.     } else {
  1277.       &tell("*\cbE\cb* You're not on any channel anyway");
  1278.     }
  1279.   } elsif ($cmd eq 'LIST') {
  1280.     &dosplat;
  1281.     $listmin=0;
  1282.     $listmax=100000;
  1283.     $listpat='';
  1284.     if ($args =~ /\*/ || $args =~ /-m[ia][nx]\s/i) {
  1285.       while (&getarg, $newarg ne '') {
  1286.     if ($newarg =~ /^-min$/i) {
  1287.       &getarg;
  1288.       $listmin=$newarg if $newarg>0;
  1289.     } elsif ($newarg =~ /^-max$/i) {
  1290.       &getarg;
  1291.       $listmax=$newarg if $newarg>0;
  1292.     } else {
  1293.       $newarg =~ s/([^\\])\./$1\\./g;
  1294.       $newarg =~ s/\*/\.\*/g;
  1295.       $newarg =~ s/([^\.\*\\\w])/\\$1/g;
  1296.       $listpat=$newarg;
  1297.     }
  1298.       }
  1299.       &sl("LIST");
  1300.     } else {
  1301.       &sl($line);
  1302.     }
  1303.   } elsif ($cmd eq 'RPING') {
  1304.     &getarg;
  1305.     &sl("RPING $newarg ".time);
  1306.   } elsif ($cmd eq 'KILL') {
  1307.     &getarg;
  1308.     if ($newarg) {
  1309.       $args || ($args=$nick);
  1310.       &sl("KILL $newarg :$args");
  1311.     } else {
  1312.       &tell("*\cbE\cb* You must specify a nick!");
  1313.     }
  1314.   } elsif ($cmd eq 'MODE' || $cmd eq 'NAMES') {
  1315.     &dosplat;
  1316.     &sl("$cmd $args");
  1317.   } elsif ($cmd eq 'OPER') {
  1318.     &getarg;
  1319.     $newarg=$nick unless $newarg;
  1320.     &getuserpass("Oper password? ", "Passwd: "), $args=$_ unless $args;
  1321.     &sl("OPER $newarg $args");
  1322.   } elsif ($cmd eq 'CONNECT') {
  1323.     &getarg;
  1324.     local($srv)=$newarg;
  1325.     &getarg;
  1326.     if ($args) {
  1327.       &sl("CONNECT $srv $newarg $args");
  1328.     } else {
  1329.       &sl("CONNECT $srv 6667 $newarg");
  1330.     }
  1331.   } elsif ($cmd eq 'SQUIT') {
  1332.     &getarg;
  1333.     &sl("SQUIT $newarg :$args");
  1334.   } elsif ($cmd eq 'WHOWAS' || $cmd eq 'ADMIN' || $cmd eq 'STATS' ||
  1335.            $cmd eq 'INFO' || $cmd eq 'LUSERS' || $cmd eq 'SQUIT' ||
  1336.        $cmd eq 'REHASH' || $cmd eq 'DIE' || $cmd eq 'LINKS' ||
  1337.        $cmd eq 'NOTE' || $cmd eq 'WALLOPS' || $cmd eq 'NICK' ||
  1338.        $cmd eq 'MOTD' || $cmd eq 'TIME' || $cmd eq 'TRACE' ||
  1339.        $cmd eq 'USERS' || $cmd eq 'SILENCE' || $cmd eq 'MAP' ||
  1340.        $cmd eq 'UPING') {
  1341.     &sl($line);
  1342.   } else {
  1343.     &tell("*\cbE\cb* Unknown command: $cmd");
  1344.   }
  1345. }
  1346.  
  1347. sub douserline {
  1348.   local($skip, $line)=(0, @_);
  1349.   if ($line =~ /^\@ssfe\@/) {
  1350.     $ssfe=$raw_mode=1;
  1351.     $add_ons.="+ssfe";
  1352.     &dostatus;
  1353.   } else {
  1354.     &dohooks("command", $line);
  1355.     return if $skip;
  1356.     if ($line =~ s/^\///) {
  1357.       &docommand($line);
  1358.     } elsif ($query ne '') {
  1359.       &msg($query, $line);
  1360.     } else {
  1361.       &say($line);
  1362.     }
  1363.   }
  1364. }
  1365.  
  1366. $ssfe_getline="`#ssfe#p";
  1367. sub getuserline {
  1368.   local($skip)='';
  1369.   &dohooks("input", $_[0], $_[1]);
  1370.   return if $skip;
  1371.   print $_[0];
  1372.   print "\n" if $raw_mode;
  1373.   print $ssfe_getline.$_[1]."\n" if $ssfe;
  1374.   while (($_=<STDIN>) ne '') {
  1375.     if (/^\@ssfe\@/) {
  1376.       $ssfe || ($add_ons.="+ssfe");
  1377.       $ssfe=$raw_mode=1;
  1378.       &dostatus;
  1379.     } else {
  1380.       &exit if $_ eq '';
  1381.       chop;
  1382.       return;
  1383.     }
  1384.   }
  1385.   &exit;
  1386. }
  1387.  
  1388. sub getuserpass {
  1389.   local($ssfe_getline)="`#ssfe#P";
  1390.   &getuserline;
  1391. }
  1392.  
  1393. %cmds=();
  1394. sub addcmd {
  1395.   local($cmd)=$_[0];
  1396.   $cmd =~ tr/a-z/A-Z/;
  1397.   $cmds{$cmd}="&cmd_".$_[0].";";
  1398. }
  1399.  
  1400. sub addhelp {
  1401.   local($cmd, $txt)=@_;
  1402.   $cmd =~ tr/A-Z/a-z/;
  1403.   push(@help, "\@".$cmd);
  1404.   foreach (split(/\n/, $txt)) {
  1405.     s/\$v/$version/g;
  1406.     s/\$d/$date/g;
  1407.     push (@help, $_);
  1408.   }
  1409. }
  1410.  
  1411. sub addset {
  1412.   local($var)=$_[0];
  1413.   $var =~ tr/a-z/A-Z/;
  1414.   $sets{$var}="set_".$_[0];
  1415. }
  1416.  
  1417. sub addsel {
  1418.   $buf_fds{$_[0]}="sel_".$_[1] if $_[2];
  1419.   $sel_fds{$_[0]}="sel_".$_[1] unless $_[2];
  1420. }
  1421.  
  1422. sub remsel {
  1423.   delete $buf_fds{$_[0]};
  1424.   delete $sel_fds{$_[0]};
  1425. }
  1426.  
  1427. @hooks=("action", "ctcp", "ctcp_reply", "dcc_chat", "dcc_request", "input",
  1428.     "invite", "join", "kick", "leave", "mode", "msg", "nick", "notice",
  1429.     "server_notice", "notify_signoff", "notify_signon", "public",
  1430.     "raw_irc", "send_action", "send_dcc_chat", "send_text", "send_notice",
  1431.     "signoff", "topic", "disconnect", "status", "print", "command",
  1432.     "chat_disconnect", "dcc_disconnect", "send_ctcp");
  1433.  
  1434. sub addhook {
  1435.   local($type, $name)=@_;
  1436.   $type =~ tr/A-Z/a-z/;
  1437.   $name="hook_".$name;
  1438.   if ($type =~ /^\d\d\d$/ || grep(($_ eq $type), @hooks)) {
  1439.     ($type =~ /^\d\d\d$/) && ($type="num_".$type);
  1440.     eval "*ugly_hack_hooks=*${type}_hooks;";
  1441.     unless (grep(($_ eq $name), @ugly_hack_hooks)) {
  1442.       push(@ugly_hack_hooks, $name);
  1443.     }
  1444.   } else {
  1445.     &tell("*\cbE\cb* $type: no such hook");
  1446.   }
  1447. }
  1448.  
  1449. sub remhook {
  1450.   local($type, $name)=@_;
  1451.   $type =~ tr/A-Z/a-z/;
  1452.   $name="hook_".$name;
  1453.   if ($type =~ /^\d\d\d$/ || grep(($_ eq $type), @hooks)) {
  1454.     ($type =~ /^\d\d\d$/) && ($type="num_".$type);
  1455.     eval "*ugly_hack_hooks=*${type}_hooks;";
  1456.     @ugly_hack_hooks=grep(($_ ne $name), @ugly_hack_hooks);
  1457.   } else {
  1458.     &tell("*\cbE\cb* $type: no such hook");
  1459.   }
  1460. }
  1461.  
  1462. sub userhost {
  1463.   push (@waituh, $_[0]);
  1464.   push (@douh, $_[1]);
  1465.   push (@erruh, $_[2]);
  1466.   &sl("USERHOST $_[0]");
  1467. }
  1468.  
  1469. sub deltimer {
  1470.   local($ref)=$_[0];
  1471.   local($i);
  1472.   if ($#trefs>=0 && $ref!=0) {
  1473.     # delete the timer if it exists
  1474.     for ($i=0; $i<$#trefs; $i++) {
  1475.       if ($trefs[$i]==$ref) {
  1476.     splice(@trefs,$i,1,splice(@trefs,-($#trefs-$i-1)));
  1477.     splice(@timers,$i,1,splice(@timers,-($#timers-$i-1)));
  1478.     splice(@timeactions,$i,1,splice(@timeactions,-($#timeactions-$i-1)));
  1479.     last;
  1480.       }
  1481.     }
  1482.     if ($trefs[$#trefs]==$ref) {
  1483.       pop(@trefs);
  1484.       pop(@timers);
  1485.       pop(@timeactions);
  1486.     }
  1487.   }
  1488. }
  1489.  
  1490. sub timer {
  1491.   local(@r, @t, @a)=();
  1492.   local($t)=$_[0]+time;
  1493.   local($ref)=$_[2] || 0;
  1494.   &deltimer($ref) if $ref;
  1495.   while ($#timers>=0 && $timers[0]<=$t) {
  1496.     push (@r, shift(@trefs));
  1497.     push (@t, shift(@timers));
  1498.     push (@a, shift(@timeactions));
  1499.   }
  1500.   @trefs=(@r, $ref, @trefs);
  1501.   @timers=(@t, $t, @timers);
  1502.   @timeactions=(@a, $_[1], @timeactions);
  1503. }
  1504.  
  1505. sub disappeared {
  1506.   local($n)=(grep(&eq($_, $_[0]), keys(%notify)));
  1507.   if ($n ne '' && $notify{$n}>0) {
  1508.     local($silent)=0;
  1509.     &dohooks("notify_signoff", $_[0]);
  1510.     &tell("*\cb(\cb* Signoff by $_[0] detected");
  1511.     $notify{$n}=0;
  1512.   }
  1513. }
  1514.  
  1515. sub appeared {
  1516.   local($t, $n)=(time, grep(&eq($_, $_[0]), keys(%notify)));
  1517.   if ($n ne '') {
  1518.     if ($notify{$n}==0) {
  1519.       local($silent)=0;
  1520.       &dohooks("notify_signon", $_[0]);
  1521.       &tell("*\cb)\cb* Signon by $_[0] detected!");
  1522.     }
  1523.     $notify{$n}=$t;
  1524.   }
  1525. }
  1526.  
  1527. $lastsendison=0;
  1528. sub send_isons {
  1529.   local($l)='';
  1530.   foreach (keys %notify) {
  1531.     &sl("ISON : $l"), $l='' if (length($l)>500);
  1532.     $l.=$_." ";
  1533.   }
  1534.   &sl("ISON :$l") if $l;
  1535.   $lastsendison=time;
  1536.   $newisons='';
  1537.   $checkisons=1;
  1538. }
  1539.  
  1540. sub signoffs {
  1541.   foreach (keys %notify) {
  1542.     if ($notify{$_}>0 && $notify{$_}<$lastsendison) {
  1543.       $notify{$_}=0;
  1544.       local($silent)=0;
  1545.       &dohooks("notify_signoff", $_);
  1546.       &tell("*\cb(\cb* Signoff by $_ detected");
  1547.     }
  1548.   }
  1549.   $checkisons='';
  1550. }
  1551.  
  1552. sub modestripper {
  1553.   local($chnl, $what)=@_;
  1554.   $chnl =~ tr/A-Z/a-z/;
  1555.   local($how, $modes, @args)=('+', split(/ +/, $what));
  1556.   foreach $m (split(//, $modes)) {
  1557.     if ($m =~ /[\-\+]/) {
  1558.       $how=$m;
  1559.     } elsif ($m =~ /[vb]/) {
  1560.       shift(@args);
  1561.     } elsif ($m eq 'k') {
  1562.       $how eq '+' ? ($chankey{$chnl}=$args[0]) : delete $chankey{$chnl};
  1563.       shift(@args);
  1564.     } elsif ($m eq 'l') {
  1565.       $how eq '+' ? ($limit{$chnl}=shift(@args)) : delete $limit{$chnl};
  1566.     } elsif ($m eq 'o') {
  1567.       $haveops{$chnl}=($how eq '+') if (&eq(shift(@args), $nick));
  1568.     } else {
  1569.       $mode{$chnl} =~ s/$m//g;
  1570.       $mode{$chnl}.=$m if $how eq '+';
  1571.     }
  1572.   }
  1573. }
  1574.  
  1575. sub umodechange {
  1576.   local($what)=@_;
  1577.   local($how)='+';
  1578.   foreach $m (split(//, $what)) {
  1579.     if ($m =~ /[\-\+]/) {
  1580.       $how=$m;
  1581.     } else {
  1582.       $umode =~ s/$m//g;
  1583.       $umode.=$m if ($how eq '+' && $m !~ /\s/);
  1584.     }
  1585.   }
  1586. }
  1587.  
  1588. sub ignored {
  1589.   foreach (@ignore) {
  1590.     return 1 if $_[0] =~ /^${_}$/;
  1591.   }
  1592.   return '';
  1593. }
  1594.  
  1595. sub dorcfile {
  1596.   return if !open(RCFILE, "<$_[0]");
  1597.   while (<RCFILE>) {
  1598.     chop;
  1599.     s/^\///;
  1600.     next if /^\#/;
  1601.     &docommand($_) if $_;
  1602.     $silent=$skip='';
  1603.   }
  1604.   close RCFILE;
  1605. }
  1606.  
  1607. sub loadrc {
  1608.   $rcloaded=1; 
  1609.   $sysrc && &dorcfile($sysrc);
  1610.   $no_rc || &dorcfile($rcfile);
  1611. }
  1612.  
  1613. sub selline {
  1614.   $leftover=0;
  1615.   $rin=$rout="\0" x 32;
  1616.   foreach ($S, 'STDIN', keys(%dcnick), keys(%buf_fds)) {
  1617.     $leftover=1, return $_ if $buffer{$_} =~ /\n/;
  1618.   }
  1619.   foreach ('STDIN', keys(%dcnick), keys(%dcwait), keys(%dgrfh), keys(%dswait),
  1620.         keys(%dsrfh), keys(%sel_fds), keys(%buf_fds)) {
  1621.     vec($rin, fileno($_), 1)=1;
  1622.   }
  1623.   vec($rin, fileno($S), 1)=1 if $connected;
  1624.   if ($#timers<0 || $timers[0]>time+30) {
  1625.     select($rout=$rin, undef, undef, 30);
  1626.   } elsif ($timers[0]<=time) {
  1627.     select($rout=$rin, undef, undef, 0);
  1628.   } else {
  1629.     select($rout=$rin, undef, undef, $timers[0]-time);
  1630.   }
  1631. }
  1632.  
  1633. sub getnick {
  1634.   &getuserline("Pick a nick: ", "Nick: ");
  1635.   $nick=$_;
  1636.   &sl("NICK $_");
  1637.   &dostatus;
  1638. }
  1639.  
  1640. sub donumeric {
  1641.   local($from)=($who eq $myserver ? '' : " (from ${who})");
  1642.   if ($cmd eq '401') {
  1643.     &yetonearg;
  1644.     &yetonearg;
  1645.     &tell("*\cb?\cb* Cannot find $newarg on irc$from");
  1646.   } elsif ($cmd eq '402') {
  1647.     &yetonearg;
  1648.     &yetonearg;
  1649.     &tell("*\cb?\cb* $newarg: no such server$from");
  1650.   } elsif ($cmd eq '403') {
  1651.     &yetonearg;
  1652.     &yetonearg;
  1653.     &tell("*\cb?\cb* $newarg: no such channel$from");
  1654.   } elsif ($cmd eq '406') {
  1655.     &yetonearg;
  1656.     &yetonearg;
  1657.     &tell("*\cb?\cb* $newarg: there was no such nickname$from");
  1658.   } elsif ($cmd eq '421') {
  1659.     &yetonearg;
  1660.     &yetonearg;
  1661.     &tell("*\cb?\cb* $newarg: unknown command$from");
  1662.   } elsif ($cmd =~ /^4[012]/) {
  1663.     $args =~ s/^[^:]*://;
  1664.     &tell("*** $args$from");
  1665.   } elsif ($cmd eq '431') {
  1666.     &tell("*** Was expecting a nickname somewhere...");
  1667.     &getnick if $connected<2;
  1668.   } elsif ($cmd eq '432') {
  1669.     if ($connected==2) {
  1670.       &tell("*\cbN\cb* Invalid nickname, you're still \"$nick\"");
  1671.     } else {
  1672.       &tell("*\cbN\cb* Invalid nickname!");
  1673.       &getnick;
  1674.     }
  1675.   } elsif ($cmd eq '433') {
  1676.     if ($connected==2) {
  1677.       &tell("*\cbN\cb* Nick already taken, you're still \"$nick\"");
  1678.     } else {
  1679.       &tell("*\cbN\cb* Nick already taken!");
  1680.       &getnick;
  1681.     }
  1682.   } elsif ($cmd eq '441') {
  1683.     local($g, $w, $c)=split(/ +/, $args);
  1684.     &tell("*\cbE\cb* $w is not on channel $c$from");
  1685.   } elsif ($cmd eq '442') {
  1686.     local($w, $c)=split(/ +/, $args);
  1687.     &tell("*\cbE\cb* You're not on channel $c$from");
  1688.   } elsif ($cmd eq '443') {
  1689.     local($w, $o, $c)=split(/ +/, $args);
  1690.     &tell("*\cbE\cb* $o is already on channel $c$from");
  1691.   } elsif ($cmd eq '465') {
  1692.     &tell("*\cbE\cb* You are banned from this server$from");
  1693.   } elsif ($cmd eq '461') {
  1694.     &yetonearg;
  1695.     &yetonearg;
  1696.     &tell("*\cbE\cb* The command $newarg needs more arguments than that$from");
  1697.   } elsif ($cmd =~ /^47[1345]$/) {
  1698.     &yetonearg;
  1699.     &yetonearg;
  1700.     local($r);
  1701.     if ($cmd eq '471') {
  1702.       $r="channel is full";
  1703.     } elsif ($cmd eq '473') {
  1704.       $r="channel is invite-only";
  1705.     } elsif ($cmd eq '474') {
  1706.       $r="banned from channel";
  1707.     } else {
  1708.       $r="bad channel key";
  1709.     }
  1710.     &tell("*\cbE\cb* Can't join $newarg: ${r}$from");
  1711.   } elsif ($cmd eq '301') {
  1712.     &yetonearg;
  1713.     &yetonearg;
  1714.     &tell("*** $newarg is away: $args");
  1715.   } elsif ($cmd eq '302') {
  1716.     &yetonearg;
  1717.     &yetonearg;
  1718.     local($n, $do, $err)=(shift(@waituh), shift(@douh), shift(@erruh));
  1719.     if ($newarg =~ /^([^\s\*=]+)[\*]?=([\-+])/) {
  1720.       $who=$1;
  1721.       local($adr)=$';
  1722.       if ($adr =~ /\@/) {
  1723.     $user=$`;
  1724.     $host=$';
  1725.       } else {
  1726.     $user=$host='';
  1727.       }
  1728.       if (&eq($who, $n)) {
  1729.     eval $do;
  1730.     $@ =~ s/\n$//, &tell("*\cbE\cb* error in userhost: $@") if $@ ne '';
  1731.       } else {
  1732.     &tell("*\cbE\cb* userhost returned for unexpected nick $who");
  1733.       }
  1734.     } else {
  1735.       if (defined($err)) {
  1736.     eval $err;
  1737.     $@ =~ s/\n$//, &tell("*\cbE\cb* error in userhost: $@") if $@ ne '';
  1738.       } else {
  1739.     &tell("*\cb?\cb* Cannot find $n on irc");
  1740.       }
  1741.     }
  1742.   } elsif ($cmd eq '303') {
  1743.     &yetonearg;
  1744.     local($n);
  1745.     foreach $n (split(/ +/, $args)) {
  1746.       &appeared($n);
  1747.     }
  1748.   } elsif ($cmd eq '305') {
  1749.     &tell("*** You are no longer marked as away");
  1750.     $away='';
  1751.     &dostatus;
  1752.   } elsif ($cmd eq '306') {
  1753.     &tell("*** You are marked as being away");
  1754.     $away=1;
  1755.     &dostatus;
  1756.   } elsif ($cmd eq '311') {
  1757.     local($g, $n, $u, $m, $g, $r)=split(/ +/, $args, 6);
  1758.     $r =~ s/^://;
  1759.     &tell("*** $n is $u\@$m ($r)");
  1760.   } elsif ($cmd eq '312') {
  1761.     &yetonearg;
  1762.     &yetonearg;
  1763.     &yetonearg;
  1764.     local($s)=$newarg;
  1765.     &tell("*** on IRC via server $s ($args)");
  1766.   } elsif ($cmd eq '313') {
  1767.     &yetonearg;
  1768.     &yetonearg;
  1769.     &tell("*** $newarg is an IRC Operator");
  1770.   } elsif ($cmd eq '314') {
  1771.     local($g, $n, $u, $m, $g, $r)=split(/ +/, $args, 6);
  1772.     $r =~ s/^://;
  1773.     &tell("*** $n was $u\@$m ($r)");
  1774.   } elsif ($cmd eq '317') {
  1775.     &yetonearg;
  1776.     &yetonearg;
  1777.     local($n)=$newarg;
  1778.     &yetonearg;
  1779.     if ($newarg>=3600) {
  1780.       &tell("*** $n has been ".int($newarg/3600)." hours, ".
  1781.       int(($newarg%3600)/60)." minutes and ".
  1782.       ($newarg%60)." seconds idle");
  1783.     } elsif ($newarg>=60) {
  1784.       &tell("*** $n has been ".int($newarg/60)." minutes and ".
  1785.         ($newarg%60)." seconds idle");
  1786.     } else {
  1787.       &tell("*** $n has been $newarg seconds idle");
  1788.     }
  1789.   } elsif ($cmd eq '319') {
  1790.     local($g, $g, $c)=split(/ +/, $args, 3);
  1791.     $c =~ s/^://;
  1792.     &tell("*** on channels: $c");
  1793.   } elsif ($cmd eq '322') {
  1794.     local($g, $c, $n, $r)=split(/ +/, $args, 4);
  1795.     $r =~ s/^://;
  1796.     $n>=$listmin && $n <=$listmax && (!$listpat || $c =~ /^${listpat}$/i)
  1797.       && &tell(sprintf("*** %-10s %-5s %s", $c, $n, $r));
  1798.   } elsif ($cmd eq '323') {
  1799.     $listmin=0;
  1800.     $listmax=100000;
  1801.     $listpat='';
  1802.   } elsif ($cmd eq '324') {
  1803.     local($g, $c, $m)=split(/ +/, $args, 3);
  1804.     $m =~ s/^://;
  1805.     $m =~ s/ $//;
  1806.     $c =~ tr/A-Z/a-z/;
  1807.     if (grep(&eq($_, $c), @channels)) {
  1808.       if (defined($mode{$c})) {
  1809.     &tell("*\cb+\cb* Mode for channel $c is \"$m\"");
  1810.       } else {
  1811.     $mode{$c}='';
  1812.       }
  1813.       &modestripper($c, $m);
  1814.       &dostatus;
  1815.     } else {
  1816.       &tell("*\cb+\cb* Mode for channel $c is \"$m\"");
  1817.     }
  1818.   } elsif ($cmd eq '329') {
  1819.     &yetonearg;
  1820.     &yetonearg;
  1821.     local($c)=$newarg;
  1822.     &yetonearg;
  1823.     local($t)=&date($newarg);
  1824.     &tell("*** $c : created $t");
  1825.   } elsif ($cmd eq '331') {
  1826.     &yetonearg;
  1827.     &yetonearg;
  1828.     &tell("*\cbT\cb* No topic is set on channel $newarg");
  1829.   } elsif ($cmd eq '332') {
  1830.     &yetonearg;
  1831.     &yetonearg;
  1832.     &tell("*\cbT\cb* Topic for $newarg: $args");
  1833.   } elsif ($cmd eq '333') {
  1834.     local($g, $c, $n, $t)=split(/ +/, $args, 4);
  1835.     local($d)=&date($t);
  1836.     &tell("*\cbT\cb* Topic for $c set by $n on $d");
  1837.   } elsif ($cmd eq '318' || $cmd eq '315' || $cmd eq '369' ||
  1838.        $cmd eq '321' || $cmd eq '366' || $cmd eq '376' ||
  1839.        $cmd eq '365' || $cmd eq '368' || $cmd eq '374' ||
  1840.        $cmd eq '219' || $cmd eq '007') {
  1841.     #nothing!
  1842.   } elsif ($cmd eq '341') {
  1843.     local($g, $n, $c)=split(/ +/, $args, 3);
  1844.     &tell("*\cbI\cb* Inviting $n to channel $c");
  1845.   } elsif ($cmd eq '352') {
  1846.     local($g, $c, $u, $m, $s, $n, $st, $g, $i)=split(/ +/, $args, 9);
  1847.     &tell(sprintf("%-10s %-9s %4s %s\@%s (%s)", $c, $n, $st, $u, $m, $i));
  1848.   } elsif ($cmd eq '353') {
  1849.     local($g, $m, $c, $r)=split(/ +/, $args, 4);
  1850.     local($n)=$nick;
  1851.     $n =~ s/(\W)/\\$1/g;
  1852.     $r =~ s/^://;
  1853.     &tell("*\cb#\cb* Users on $c: $r");
  1854.     $c =~ tr/A-Z/a-z/;
  1855.     $haveops{$c}=1 if ($r =~ /\@${n}\b/i);
  1856.     &dostatus if &eq($c, $talkchannel);
  1857.   } elsif ($cmd eq '221') {
  1858.     &yetonearg;
  1859.     &tell("*\cb+\cb* Your user mode is \"$args\"");
  1860.   } elsif ($cmd eq '200') {
  1861.     local($b, $l, $v, $n, $s)=split(/ +/, $args);
  1862.     $s =~ s/^://;
  1863.     &tell("*** $l $who ($v) ==> $n $s");
  1864.   } elsif ($cmd eq '205') {
  1865.     local($b, $u, $h, $n)=split(/ +/, $args);
  1866.     $n =~ s/^://;
  1867.     &tell("*** $u [$h] ==> $n");
  1868.   } elsif ($cmd =~ /^20/) {
  1869.     local($b, $t, $n, $r)=split(/ +/, $args, 4);
  1870.     &tell("*** $t [$n] ==> $r");
  1871.   } elsif ($cmd eq '375' || $cmd eq '372' || $cmd =~ /^25/) {
  1872.     &yetonearg;
  1873.     &tell("*** $args");
  1874.   } else {
  1875.     &yetonearg;
  1876.     #$args =~ s/ :/ /;
  1877.     &tell("*** $args$from");
  1878.   }
  1879. }
  1880.  
  1881. # main prog
  1882.  
  1883. print "`#ssfe#i\n" unless (-t STDOUT);
  1884. &tell("*** Welcome to \cbsirc\cb version $version; type /help for help");
  1885.  
  1886. &load($sysinit) if $sysinit ne '' && -f $sysinit;
  1887. &load($initfile) if !$restrict && $initfile ne '' && -f $initfile;
  1888.  
  1889. &bindtoserver;
  1890. while (1) {
  1891.   $silent=$skip='';
  1892.   if ($connected==2) {
  1893.     $time=time;
  1894.     &loadrc unless $rcloaded;
  1895.     &send_isons
  1896.       if $time>=$lastsendison+90 || ($newisons && $time>=$lastsendison+10);
  1897.     &signoffs if $checkisons && ($time>=$lastsendison+30);
  1898.   }
  1899.   $fh=&selline;
  1900.   foreach $rfh (keys (%buf_fds)) {
  1901.     if (vec($rout, fileno($rfh), 1) || ($leftover && $fh eq $rfh)) {
  1902.       &gl($rfh) || next;
  1903.       local($line, $h)=($_, $buf_fds{$rfh});
  1904.       delete $buf_fds{$rfh}, delete $buffer{$rfh}, close($rfh) if $_ eq '';
  1905.       eval { &$h($line); };
  1906.       $@ =~ s/\n$//, &tell("*\cbE\cb* error in buffered fd hook &$h: $@")
  1907.     if $@ ne '';
  1908.     }
  1909.   }
  1910.   foreach $rfh (keys (%sel_fds)) {
  1911.     if (vec($rout, fileno($rfh), 1)) {
  1912.       local($h)=$sel_fds{$rfh};
  1913.       eval { &$h(); };
  1914.       $@ =~ s/\n$//, &tell("*\cbE\cb* error in unbuffered fd hook &$h: $@")
  1915.     if $@ ne '';
  1916.     }
  1917.   }
  1918.   foreach $rfh (keys (%dcnick)) {
  1919.     if (vec($rout, fileno($rfh), 1) || ($leftover && $fh eq $rfh)) {
  1920.       &gl($rfh) || next;
  1921.       &dcerror($rfh), next if $_ eq '';
  1922.       chop;
  1923.       local($who, $what)=($dcnick{$rfh}, $_);
  1924.       $dcvol{$dcnick{$rfh}}+=length($what);
  1925.       print "`#ssfe#t/m =$who \n" if $ssfe;
  1926.       print "`#ssfe#o=${who}= $what\n" if $ssfe;
  1927.       &dohooks("dcc_chat", $who, $what);
  1928.       &tell("=\cb${who}\cb= $what");
  1929.       $silent='';
  1930.     }
  1931.   }
  1932.   foreach $rfh (keys (%dcwait)) {
  1933.     if (vec($rout, fileno($rfh), 1)) {
  1934.       local($n, $fh);
  1935.       if (&accept($fh, $rfh)) {
  1936.     select($fh); $|=1; select(STDOUT);
  1937.     $n=$dcwait{$rfh};
  1938.     $dcnick{$fh}=$n;
  1939.     $n =~ tr/A-Z/a-z/;
  1940.     $dcvol{$n}=0;
  1941.     $dcfh{$n}=$fh;
  1942.     &tell("*\cbD\cb* DCC CHAT connection with $n established");
  1943.     print "`#ssfe#t/m =$n \n" if $ssfe;
  1944.       }
  1945.       delete $dcwait{$rfh};
  1946.     }
  1947.   }
  1948.   foreach $sfh (keys (%dswait)) {
  1949.     local($rfh, $fh)=$dswait{$sfh};
  1950.     if (vec($rout, fileno($sfh), 1)) {
  1951.       if (&accept($fh, $sfh)) {
  1952.     select($fh); $|=1; select(STDOUT);
  1953.     $dsrfh{$fh}=$rfh;
  1954.     $dstarttime{$rfh}=time;
  1955.     $dtransferred{$fh}=0;
  1956.     $dnick{$fh}=$dnick{$sfh};
  1957.     &tell("*\cbD\cb* DCC SEND connection with $dnick{$sfh} established");
  1958.       }
  1959.       delete $dnick{$sfh};
  1960.       delete $dswait{$sfh};
  1961.     }
  1962.   }
  1963.   foreach $sfh (keys (%dgrfh)) {
  1964.     local($rfh)=$dgrfh{$sfh};
  1965.     if (vec($rout, fileno($sfh), 1)) {
  1966.       local($a, $buf)=(0, '');
  1967.       $a=sysread($sfh, $buf, 4096);
  1968.       if ($a) {
  1969.     $dtransferred{$sfh}+=$a;
  1970.     print $rfh $buf;
  1971.     print $sfh pack("N", $dtransferred{$sfh});
  1972.       } else {
  1973.     &dgsclose($sfh, $rfh);
  1974.       }
  1975.     }
  1976.   }
  1977.   foreach $sfh (keys (%dsrfh)) {
  1978.     local($rfh)=$dsrfh{$sfh};
  1979.     if (vec($rout, fileno($sfh), 1) || !$dtransferred{$sfh}) {
  1980.       local($ack, $csa, $buf, $b, $l, $w)=(0, '', '');
  1981.       if ($dtransferred{$sfh}) {
  1982.     &dgsclose($sfh, $rfh), next if sysread($sfh, $b, 4)!=4;
  1983.     $ack=unpack("N", $b);
  1984.       }
  1985.       $csa=$set{"SENDAHEAD"}-$dtransferred{$sfh}+$ack;
  1986.       next if $csa<0;
  1987.       $l=read($rfh, $buf, 512+$csa);
  1988.       $w=syswrite($sfh, $buf, $l) if $l;
  1989.       next if $l==0 && $ack<$dtransferred{$sfh};
  1990.       $dtransferred{$sfh}+=$w;
  1991.       &dgsclose($sfh, $rfh), next if ($w<$l || $l==0);
  1992.     }
  1993.   }
  1994.   while ($#timers>=0 && $timers[0]<=time) {
  1995.     shift (@timers);
  1996.     eval shift (@timeactions);
  1997.     $@ =~ s/\n$//, &tell("*\cbE\cb* error in timer: $@") if $@ ne '';
  1998.   }
  1999.   if (vec($rout, fileno(STDIN), 1) || ($leftover && $fh eq 'STDIN')) {
  2000.     &gl('STDIN') || next;
  2001.     &exit if $_ eq '';
  2002.     chop;
  2003.     &douserline($_) if $_ ne '';
  2004.   }
  2005.   if ($connected && (($leftover && $fh eq $S) || vec($rout, fileno($S), 1))) {
  2006.     &gl($S) || next;
  2007.     if ($_ eq '') {
  2008.       &tell("*\cbE\cb* Connection to server lost");
  2009.       close($S);
  2010.       delete $buffer{$S};
  2011.       $connected=0;
  2012.       &dohooks("disconnect");
  2013.       next;
  2014.     }
  2015.     chop;
  2016.     $serverline=$_;
  2017.     $_=$server." ".$_ unless /^:/;
  2018.     ($who, $cmd, $args)=split(/ /, $_, 3);
  2019.     $cmd =~ tr/a-z/A-Z/;
  2020.     $who =~ s/^://;
  2021.     $args =~ s/^://;
  2022.     $user=$host=$puh1=$puh2='';
  2023.     if ($who =~ /^([^!@ ]+)!([^@ ]+)@([^ ]+)$/) {
  2024.       ($who, $user, $host) = ($1, $2, $3);
  2025.       $puh1="!$user\@$host" if $set{"PRINTUH"} ne 'none';
  2026.       $puh2=$puh1 if $set{"PRINTUH"} eq 'all';
  2027.     }
  2028.     &dohooks("raw_irc", $cmd, $args);
  2029.     next if $skip;
  2030.     next if (($cmd eq 'PRIVMSG' || $cmd eq 'NOTICE') &&
  2031.         &ignored("$who!$user\@$host"));
  2032.     if ($cmd eq '001') {
  2033.       $connected=2;
  2034.       $myserver=$who;
  2035.       ($nick)=split(/ /, $args, 2);
  2036.     }
  2037.     if ($cmd =~ /^\d\d\d$/) {
  2038.       &dohooks("num_".$cmd, $args);
  2039.       next if $skip;
  2040.       &donumeric;
  2041.     } elsif ($cmd eq 'PING') {
  2042.       &sl("PONG $args");
  2043.     } elsif  ($cmd eq 'PRIVMSG') {
  2044.       &yetonearg;
  2045.       if ($args =~ /^\001([^\001]*)\001$/ && $set{'CTCP'} ne 'none') {
  2046.     &ctcp($newarg, $1);
  2047.       } elsif (!$printchan && &eq($newarg, $talkchannel)) {
  2048.     &dohooks("public", $newarg, $args);
  2049.     &tell("<${who}> $args");
  2050.       } elsif ($newarg =~ /^[\#\&\+]/) {
  2051.     &dohooks("public", $newarg, $args);
  2052.     &tell("<${who}:${newarg}> $args");
  2053.       } elsif (&eq ($newarg, $nick)) {
  2054.     print "`#ssfe#t/m $who \n" if $ssfe;
  2055.     print "`#ssfe#o[$who$puh1] $args\n" if $ssfe;
  2056.     &dohooks("msg", $args);
  2057.     &tell("[\cb${who}\cb${puh1}] $args");
  2058.       } else {
  2059.     &tell("[\cb${who}\cb${puh1}:${newarg}\cb] $args");
  2060.       }
  2061.     } elsif ($cmd eq 'NOTICE') {
  2062.       &yetonearg;
  2063.       if ($args =~ /^\001([^\001]*)\001$/) {
  2064.     &ctcpreply($newarg, $1);
  2065.       } elsif ($newarg =~ /^[\#\&\+]/) {
  2066.     &dohooks("notice", $newarg, $args);
  2067.     &tell("-${who}/${newarg}- $args");
  2068.       } elsif ($who =~ /\./) {
  2069.     &dohooks("server_notice", $args);
  2070.         $args="*** ".$args unless ($args =~ /^\*/);
  2071.     &tell($args);
  2072.       } elsif (&eq($newarg, $nick)) {
  2073.     &dohooks("notice", $newarg, $args);
  2074.     &tell("-\cb${who}\cb${puh1}- $args");
  2075.       } else {
  2076.     &dohooks("notice", $newarg, $args);
  2077.     &tell("-\cb$who$puh1:${newarg}\cb- $args");
  2078.       }
  2079.     } elsif ($cmd eq 'KICK') {
  2080.       &yetonearg;
  2081.       local($channel)=$newarg;
  2082.       &yetonearg;
  2083.       $args=$who unless $args;
  2084.       if (&eq($nick, $newarg)) {
  2085.     &tell("*\cb<\cb* You have been kicked off channel $channel by $who$puh2 ($args)");
  2086.     @channels=grep(!&eq($_, $channel), @channels);
  2087.     if (@channels) {
  2088.       $talkchannel=$channels[$#channels];
  2089.     } else {
  2090.       $talkchannel='';
  2091.     }
  2092.     $channel =~ tr/A-Z/a-z/;
  2093.     &dohooks("kick", $newarg, $channel, $args);
  2094.     delete $mode{$channel};
  2095.     delete $limit{$channel};
  2096.     delete $haveops{$channel};
  2097.     delete $chankey{$channel};
  2098.     $talkchannel && !$ssfe && &tell("*** Talking to $talkchannel now");
  2099.     &dostatus;
  2100.       } else {
  2101.     &dohooks("kick", $newarg, $channel, $args);
  2102.     &tell("*\cb<\cb* $newarg has been kicked off channel $channel by $who$puh2 ($args)");
  2103.       }
  2104.     } elsif ($cmd eq 'PART') {
  2105.       &yetonearg;
  2106.       if (&eq($who, $nick)) {
  2107.     &tell("*\cb<\cb* You have left channel $newarg");
  2108.     @channels=grep(!&eq($_, $newarg), @channels);
  2109.     if (@channels) {
  2110.       $talkchannel=$channels[$#channels];
  2111.     } else {
  2112.       $talkchannel='';
  2113.     }
  2114.     $newarg =~ tr/A-Z/a-z/;
  2115.     delete $mode{$newarg};
  2116.     delete $limit{$newarg};
  2117.     delete $haveops{$newarg};
  2118.     delete $chankey{$newarg};
  2119.     &dohooks("leave", $newarg);
  2120.     $talkchannel && !$ssfe && &tell("*** Talking to $talkchannel now");
  2121.     &dostatus;
  2122.       } else {
  2123.     &dohooks("leave", $newarg);
  2124.     &tell("*\cb<\cb* $who$puh2 has left channel $newarg");
  2125.       }
  2126.     } elsif ($cmd eq 'JOIN') {
  2127.       &yetonearg;
  2128.       if (&eq($nick, $who)) {
  2129.     push(@channels, $newarg);
  2130.     $talkchannel=$newarg;
  2131.     &dohooks("join", $newarg);
  2132.     &dostatus;
  2133.     &tell("*\cb>\cb* You have joined channel $newarg");
  2134.     &sl("MODE $newarg");
  2135.       } else {
  2136.     &dohooks("join", $newarg);
  2137.     &tell("*\cb>\cb* $who ($user\@$host) has joined channel $newarg");
  2138.       }
  2139.       &appeared($who);
  2140.     } elsif ($cmd eq 'NICK') {
  2141.       &yetonearg;
  2142.       if (&eq($nick, $who)) {
  2143.     $nick=$newarg;
  2144.     &dohooks("nick", $newarg);
  2145.     $who=$newarg;
  2146.     &dostatus;
  2147.     &tell("*\cbN\cb* You are now known as $newarg");
  2148.       } else {
  2149.     &dohooks("nick", $newarg);
  2150.     &tell("*\cbN\cb* $who$puh2 is now known as $newarg");
  2151.       }
  2152.     } elsif ($cmd eq 'MODE') {
  2153.       &yetonearg;
  2154.       $args =~ s/ $//;
  2155.       if ($newarg =~ /^[\#\&\+]/) {
  2156.     &modestripper($newarg, $args);
  2157.     &dohooks("mode", $newarg, $args);
  2158.     &dostatus;
  2159.     &tell("*\cb+\cb* Mode change \"$args\" on channel $newarg by $who$puh2");
  2160.       } else {
  2161.     local($towho)=$newarg;
  2162.     &yetonearg;
  2163.     &umodechange($newarg), &dostatus if &eq($towho, $nick);
  2164.     &dohooks("mode", $towho, $newarg);
  2165.     &tell("*\cb+\cb* Mode change \"$newarg\" for user $towho by $who");
  2166.       }
  2167.     } elsif ($cmd eq 'KILL') {
  2168.       &yetonearg;
  2169.       local($n)=$newarg;
  2170.       $args || ($args=$who);
  2171.       &tell("*\cb<\cb* $n got killed by $who$puh1 ($args)");
  2172.     } elsif ($cmd eq 'INVITE') {
  2173.       &yetonearg;
  2174.       &yetonearg;
  2175.       &dohooks("invite", $newarg);
  2176.       $invited=$newarg;
  2177.       &tell("*\cbI\cb* $who$puh1 invites you to channel $newarg");
  2178.     } elsif ($cmd eq 'TOPIC') {
  2179.       &yetonearg;
  2180.       &dohooks("topic", $newarg, $args);
  2181.       &tell("*\cbT\cb* $who$puh2 has changed the topic on channel $newarg to \"$args\"");
  2182.     } elsif ($cmd eq 'SILENCE') {
  2183.       &tell("*** Silence $args");
  2184.     } elsif ($cmd eq 'PONG') {
  2185.     } elsif ($cmd eq 'QUIT') {
  2186.       &dohooks("signoff", $args);
  2187.       &tell("*\cb<\cb* Signoff: $who$puh2 ($args)");
  2188.       &disappeared($who);
  2189.     } elsif ($cmd eq 'WALLOPS') {
  2190.       &tell("!$who$puh2! ".$args);
  2191.     } elsif ($cmd eq 'RPONG') {
  2192.       local($n, $t, $ms, $ts)=split(/ +/, $args);
  2193.       $ts =~ s/^://;
  2194.       &tell("*** RPONG: $who - $t: $ms ms, ".time-$ts." sec");
  2195.     } else {
  2196.       &tell("*** The server says: $serverline");
  2197.     }
  2198.   }
  2199. }
  2200.  
  2201.