[IRC-Dev CVS] [CVS] Module ircd-ircdev: Change committed
Brought to you by:
zolty
From: Toni G. <zo...@us...> - 2005-05-16 19:51:49
|
CVSROOT : /cvsroot/irc-dev Module : ircd-ircdev Commit time: 2005-05-16 11:31:30 UTC Modified files: ChangeLog ChangeLog.es include/patchlevel.h Added files: ircd/test/channel-1.cmd ircd/test/client-1.cmd ircd/test/die.cmd ircd/test/gline-1.cmd ircd/test/ircd-t1.conf ircd/test/run-tests.sh ircd/test/stats-1.cmd ircd/test/test-driver.pl Log message: Author: zoltan <zo...@ir...> Log message: 2005-05-16 Toni García <zo...@ir...> 1.0.alpha35 * Utilidades de Testing ---------------------- diff included ---------------------- Index: ircd-ircdev/ChangeLog diff -u ircd-ircdev/ChangeLog:1.36 ircd-ircdev/ChangeLog:1.37 --- ircd-ircdev/ChangeLog:1.36 Mon May 16 04:22:48 2005 +++ ircd-ircdev/ChangeLog Mon May 16 04:31:19 2005 @@ -1,10 +1,13 @@ # # ChangeLog for ircd-ircdev # -# $Id: ChangeLog,v 1.36 2005/05/16 11:22:48 zolty Exp $ +# $Id: ChangeLog,v 1.37 2005/05/16 11:31:19 zolty Exp $ # # Insert new changes at beginning of the change list. # +2005-05-16 Toni García <zo...@ir...> 1.0.alpha35 + * Testing tools + 2005-05-16 Toni García <zo...@ir...> 1.0.alpha34 * Undernet synchronization Index: ircd-ircdev/ChangeLog.es diff -u ircd-ircdev/ChangeLog.es:1.36 ircd-ircdev/ChangeLog.es:1.37 --- ircd-ircdev/ChangeLog.es:1.36 Mon May 16 04:22:48 2005 +++ ircd-ircdev/ChangeLog.es Mon May 16 04:31:19 2005 @@ -1,10 +1,13 @@ # # Log de Cambios para ircd-ircdev # -# $Id: ChangeLog.es,v 1.36 2005/05/16 11:22:48 zolty Exp $ +# $Id: ChangeLog.es,v 1.37 2005/05/16 11:31:19 zolty Exp $ # # Insertar los nuevos cambios al principio de esta lista de cambios. # +2005-05-16 Toni García <zo...@ir...> 1.0.alpha35 + * Utilidades de Testing + 2005-05-16 Toni García <zo...@ir...> 1.0.alpha34 * Sincronización Undernet Index: ircd-ircdev/include/patchlevel.h diff -u ircd-ircdev/include/patchlevel.h:1.35 ircd-ircdev/include/patchlevel.h:1.36 --- ircd-ircdev/include/patchlevel.h:1.35 Mon May 16 04:22:50 2005 +++ ircd-ircdev/include/patchlevel.h Mon May 16 04:31:20 2005 @@ -17,10 +17,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * $Id: patchlevel.h,v 1.35 2005/05/16 11:22:50 zolty Exp $ + * $Id: patchlevel.h,v 1.36 2005/05/16 11:31:20 zolty Exp $ * */ -#define PATCHLEVEL ".alpha34" +#define PATCHLEVEL ".alpha35" #define RELEASE "1.0" Index: ircd-ircdev/ircd/test/channel-1.cmd diff -u /dev/null ircd-ircdev/ircd/test/channel-1.cmd:1.1 --- /dev/null Mon May 16 04:31:30 2005 +++ ircd-ircdev/ircd/test/channel-1.cmd Mon May 16 04:31:20 2005 @@ -0,0 +1,48 @@ +define srv localhost:7701 + +connect cl1 Alex alex %srv% :Test client 1 +connect cl2 Bubb bubb %srv% :Test client 2 +:cl1 join #test +:cl1 join #test2 +:cl1 mode #test +bb *!*@127.0.0.1 *!*@127.0.0.2 +:cl2 wait cl1 +:cl2 join #test +:cl1 wait cl2 +:cl1 invite Bubb #test +:cl2 expect *cl1 invite #test +:cl2 join #test +:cl2 privmsg #test :Hello, *cl1. +:cl2 nick Buba +:cl2 mode #test +l 15 +:cl1 wait cl2 +:cl1 privmsg #test :Hello, *cl2. +:cl1 mode #test -b+kv *!*@127.0.0.1 secret Bubb +:cl1 mode #test +b foo!bar@baz +:cl1 mode #test +b +:cl1 mode #test : +:cl1 mode #test +:cl1 raw who #test %lfuh +:cl2 wait cl1 +:cl2 part #test +:cl1 wait cl2 +:cl2 join #test public +:cl2 join #test secret +:cl1 join 0 +:cl1 join #test2 +:cl2 wait cl1 +:cl2 join #test2 +:cl1 wait cl2 +:cl1 mode #test2 +smtinrDlAU 15 apples oranges +:cl1 mode #test2 +:cl2 wait cl1 +:cl2 join #test2 apples +:cl2 privmsg #test2 :Hello, oplevels. +:cl2 mode #test2 +:cl2 mode #test2 -io+v Alex Alex +:cl1 wait cl2 +:cl1 part #test2 +:cl1 join #test2 +:cl2 wait cl1 +:cl2 mode #test2 -D +:cl2 mode #test +v Alex +:cl1 wait cl2 Index: ircd-ircdev/ircd/test/client-1.cmd diff -u /dev/null ircd-ircdev/ircd/test/client-1.cmd:1.1 --- /dev/null Mon May 16 04:31:30 2005 +++ ircd-ircdev/ircd/test/client-1.cmd Mon May 16 04:31:20 2005 @@ -0,0 +1,15 @@ +define srv localhost:7701 + +connect cl1 Alex alex %srv% :Test client 1 +:cl1 oper oper1 oper1 +connect cl2 Bubb bubb %srv% :Test client 2 +:cl2 oper oper3 oper4 +:cl2 oper oldoper wrongpass +:cl2 oper md5oper wrongpass +:cl2 oper cryptoper wrongpass +:cl2 oper oper2 oper2 +:cl2 raw :privs Alex Alex +:cl1 wait cl2 +:cl1 raw :privs Bubb +:cl1 nick A +:cl1 nick Alexey Index: ircd-ircdev/ircd/test/die.cmd diff -u /dev/null ircd-ircdev/ircd/test/die.cmd:1.1 --- /dev/null Mon May 16 04:31:30 2005 +++ ircd-ircdev/ircd/test/die.cmd Mon May 16 04:31:20 2005 @@ -0,0 +1,5 @@ +connect cl1 Alex alex localhost:7701 :Test client 1 +:cl1 oper oper1 oper1 +:cl1 raw :restart brb +:cl1 oper oper1 oper1 +:cl1 raw :die :testing over Index: ircd-ircdev/ircd/test/gline-1.cmd diff -u /dev/null ircd-ircdev/ircd/test/gline-1.cmd:1.1 --- /dev/null Mon May 16 04:31:30 2005 +++ ircd-ircdev/ircd/test/gline-1.cmd Mon May 16 04:31:20 2005 @@ -0,0 +1,26 @@ +define srv localhost:7701 + +connect cl1 Alex alex %srv% :Test client 1 +:cl1 oper oper1 oper1 +:cl1 raw :gline !+$Rbubb 30 :Bubb is not welcome here +:cl1 sleep 35 +:cl1 raw :gline !+127.2.* 3600 :Localclone? +:cl1 sleep 5 +:cl1 raw :gline !+127.2.* 3600 :Localclone? +connect cl2 Bubb bubb %srv% :Test client 2 +:cl1 raw :gline +:cl1 raw :gline $Rbubb +:cl1 raw :gline -$Rbubb +:cl1 wait cl2 +:cl1 raw :gline !+$Rbubb * 3600 :Bubb is not welcome here +:cl1 sleep 5 +:cl1 raw :gline -$Rbubb +:cl1 raw :gline +#warez 30 :Warez r bad mmkay +:cl2 wait cl1 +:cl2 join #warez +:cl1 sleep 35 +:cl1 raw :stats glines +:cl1 raw :gline !+*@127.0.0.2 3600 :Localclone? +:cl1 raw :gline !+127.1.* 3600 :Localclone? +:cl1 raw :stats memory +:cl2 raw :gline Index: ircd-ircdev/ircd/test/ircd-t1.conf diff -u /dev/null ircd-ircdev/ircd/test/ircd-t1.conf:1.1 --- /dev/null Mon May 16 04:31:30 2005 +++ ircd-ircdev/ircd/test/ircd-t1.conf Mon May 16 04:31:20 2005 @@ -0,0 +1,97 @@ +General { + name = "test-1.example.net"; + vhost = "127.0.0.1"; + vhost = "::1"; + description = "Test Server 1"; + numeric = 1; +}; + +Admin { + location = "Somewhere"; + contact = "Someone"; +}; + +Class { + name = "Server"; + pingfreq = 180 seconds; + connectfreq = 300 seconds; + maxlinks = 1; + sendq = 9000000; +}; + +Class { + name = "others"; + pingfreq = 180 seconds; + sendq = 160000; + maxlinks = 100; + usermode = "+oiwx"; +}; + +Class { + name = "Opers"; + pingfreq = 180 seconds; + sendq = 160000; + maxlinks = 10; + local = no; +}; + +Connect { + name = "bogus.example.net"; + host = "example.net"; + password = "bogus_example"; + port = 7700; + class = "Server"; + maxhops = 2; + hub = "*.example.net"; + autoconnect = yes; # forces a DNS resolution attempt +}; + +CRule { + server = "bogus.example.net"; + all = yes; + rule = "connected(*)"; +}; + +CRule { + server = "bogus.example.net"; + all = no; + rule = "directcon(*)"; +}; + +UWorld { + name = "uworld.example.net"; + name = "uworld2.example.net"; +}; + +Jupe { + nick = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q"; + nick = "R,S,T,U,V,W,X,Y,Z,{,|,},~,-,_,`"; +}; + +Operator { name = "oper1"; host = "*@*"; password = "$PLAIN$oper1"; class = "Opers"; }; +Operator { name = "oper2"; host = "*@*"; password = "$PLAIN$oper2"; class = "Opers"; local = yes; }; +Operator { name = "oldoper"; host = "*@*"; password = "Xlfc26b4eYGWs"; class = "Opers"; }; +Operator { name = "md5oper"; host = "*@*"; password = "$SMD5$2O$4O.rSAmhE4Fg05MmG.047/"; class = "Opers"; }; +Operator { name = "cryptoper"; host = "*@*"; password = "$CRYPT$41ndrxPQu3B66"; class = "Opers"; }; + +Kill { username = "sub7"; realname = "s*7*"; reason = "You are infected with a Trojan"; }; +Kill { realname = "Chloe"; reason = "drones"; }; +Kill { username = "sub7"; reason = "You are infected with a Trojan"; }; + +Client { class = "others"; ip = "*"; }; + +Port { server = yes; port = 7700; }; +Port { server = no; port = 7701; }; + +Quarantine { + "#shells" = "Thou shalt not support the h4><0rz"; +}; + +Pseudo "X" { + name = "X"; + nick = "X...@ch..."; +}; + +Features { + "HIS_STATS_k" = "FALSE"; +}; Index: ircd-ircdev/ircd/test/run-tests.sh diff -u /dev/null ircd-ircdev/ircd/test/run-tests.sh:1.1 --- /dev/null Mon May 16 04:31:30 2005 +++ ircd-ircdev/ircd/test/run-tests.sh Mon May 16 04:31:20 2005 @@ -0,0 +1,12 @@ +#! /bin/sh +set -e +srcdir=$1 +for script in channel-1 client-1 stats-1 gline-1 ; do + echo "Running test $script." + ${srcdir}/test-driver.pl ${srcdir}/${script}.cmd +done +echo "Terminating server." +${srcdir}/test-driver.pl ${srcdir}/die.cmd +../ircd -? +../ircd -v +../ircd -x 6 -k -d ${srcdir} -f ircd-t1.conf -c user@127.0.0.1 Index: ircd-ircdev/ircd/test/stats-1.cmd diff -u /dev/null ircd-ircdev/ircd/test/stats-1.cmd:1.1 --- /dev/null Mon May 16 04:31:30 2005 +++ ircd-ircdev/ircd/test/stats-1.cmd Mon May 16 04:31:20 2005 @@ -0,0 +1,91 @@ +# Connect to server +connect cl1 Alex alex localhost:7701 :Test client 1 +:cl1 oper oper1 oper1 + +# Single letter stats commands +:cl1 raw :stats a +:cl1 raw :stats c +:cl1 raw :stats d +:cl1 raw :stats D +:cl1 raw :stats e +:cl1 raw :stats f +:cl1 raw :stats g +:cl1 raw :stats i +:cl1 raw :stats j +:cl1 raw :stats J +:cl1 raw :stats k +:cl1 raw :stats l +:cl1 raw :stats L +:cl1 raw :stats m +:cl1 raw :stats o +:cl1 raw :stats p +:cl1 raw :stats q +:cl1 raw :stats r +:cl1 raw :stats R +:cl1 raw :stats t +:cl1 raw :stats T +:cl1 raw :stats u +:cl1 raw :stats U +:cl1 raw :stats v +:cl1 raw :stats V +:cl1 raw :stats w +:cl1 raw :stats x +:cl1 raw :stats z +:cl1 raw :stats * + +# Named stats commands +:cl1 raw :stats nameservers +:cl1 raw :stats connect +:cl1 raw :stats maskrules +:cl1 raw :stats crules +:cl1 raw :stats engine +:cl1 raw :stats features +:cl1 raw :stats glines +:cl1 raw :stats access +:cl1 raw :stats histogram +:cl1 raw :stats jupes +:cl1 raw :stats klines +:cl1 raw :stats links +:cl1 raw :stats modules +:cl1 raw :stats commands +:cl1 raw :stats operators +:cl1 raw :stats ports +:cl1 raw :stats quarantines +:cl1 raw :stats mappings +:cl1 raw :stats usage +:cl1 raw :stats motds +:cl1 raw :stats locals +:cl1 raw :stats uworld +:cl1 raw :stats uptime +:cl1 raw :stats vservers +:cl1 raw :stats vserversmach +:cl1 raw :stats userload +:cl1 raw :stats memusage +:cl1 raw :stats classes +:cl1 raw :stats memory +:cl1 raw :stats help +:cl1 raw :hash +:cl1 raw :rehash +:cl1 nick Alexey + +# Varparam stats +:cl1 raw :stats access * 127.0.0.1 +:cl1 raw :stats access * * +:cl1 raw :stats klines * * +:cl1 raw :stats klines * *@* +:cl1 raw :stats links * * +:cl1 raw :stats ports * 7700 +:cl1 raw :stats quarantines * #frou-frou +:cl1 raw :stats vservers * *.example.net + +# Invalid or nonexistent stats requests +:cl1 raw :stats y +:cl1 raw :stats ÿ +:cl1 raw :stats mºDãç +:cl1 raw :stats long_garbage_here_to_hopefully_trigger_the_core_reported_by_dan + +# Drop oper status and try a few others +:cl1 mode Alex -o +:cl1 raw :stats k +:cl1 raw :stats k * * +:cl1 raw :stats k * *@* Index: ircd-ircdev/ircd/test/test-driver.pl diff -u /dev/null ircd-ircdev/ircd/test/test-driver.pl:1.1 --- /dev/null Mon May 16 04:31:30 2005 +++ ircd-ircdev/ircd/test/test-driver.pl Mon May 16 04:31:20 2005 @@ -0,0 +1,541 @@ +#! /usr/bin/perl -wT + +# If you edit this file, please check carefully that the garbage +# collection isn't broken. POE is sometimes too clever for our good +# in finding references to sessions, and keeps running even after we +# want to stop. +# $Id: test-driver.pl,v 1.1 2005/05/16 11:31:20 zolty Exp $ + +# This interprets a simple scripting language. Lines starting with a +# hash mark (#, aka octothorpe, pound sign, etc) are ignored. The +# special commands look like this, where angle brackets indicate a +# metavariable: +# define <macro> <value> +# undef <macro> +# connect <name> <nick> <ident> <server> :<userinfo> +# sync <name1>,<name2>[,<name3>]* +# :<name> <command>[ <args]* +# For the last line syntax, <command> may be an IRC or IRC-like +# command. Supported non-IRC commands are: +# :<name> expect <source|*name2> [...] +# :<name> raw <text> +# :<name> sleep <seconds> +# :<name> wait <name2> + +require 5.006; + +use bytes; +use warnings; +use strict; +use vars; +use constant DELAY => 2; +use constant EXPECT_TIMEOUT => 15; +use constant RECONNECT_TIMEOUT => 5; +use constant THROTTLED_TIMEOUT => 90; + +use FileHandle; +use POE; +use POE::Component::IRC; + +# this defines commands that take "zero time" to execute +# (specifically, those which do not send commands from the issuing +# client to the server) +our $zero_time = { + expect => 1, + sleep => 1, + wait => 1, + }; + +# Create the main session and start POE. +# All the empty anonymous subs are just to make POE:Session::ASSERT_STATES happy. +POE::Session->create(inline_states => + { + # POE kernel interaction + _start => \&drv_start, + _child => sub {}, + _stop => sub { + my $heap = $_[HEAP]; + print "\nThat's all, folks!"; + print "(exiting at line $heap->{lineno}: $heap->{line})" + if $heap->{line}; + print "\n"; + }, + _default => \&drv_default, + # generic utilities or miscellaneous functions + heartbeat => \&drv_heartbeat, + timeout_expect => \&drv_timeout_expect, + reconnect => \&drv_reconnect, + enable_client => sub { $_[ARG0]->{ready} = 1; }, + disable_client => sub { $_[ARG0]->{ready} = 0; }, + die => sub { $_[KERNEL]->signal($_[SESSION], 'TERM'); }, + # client-based command issuers + cmd_die => \&cmd_generic, + cmd_expect => \&cmd_expect, + cmd_invite => \&cmd_generic, + cmd_join => \&cmd_generic, + cmd_mode => \&cmd_generic, + cmd_nick => \&cmd_generic, + cmd_notice => \&cmd_message, + cmd_oper => \&cmd_generic, + cmd_part => \&cmd_generic, + cmd_privmsg => \&cmd_message, + cmd_quit => \&cmd_generic, + cmd_raw => \&cmd_raw, + cmd_sleep => \&cmd_sleep, + cmd_wait => \&cmd_wait, + # handlers for messages from IRC + irc_001 => \&irc_connected, # Welcome to ... + irc_snotice => sub {}, # notice from a server (anonymous/our uplink) + irc_notice => \&irc_notice, # NOTICE to self or channel + irc_msg => \&irc_msg, # PRIVMSG to self + irc_public => \&irc_public, # PRIVMSG to channel + irc_connected => sub {}, + irc_ctcp_action => sub {}, + irc_ctcp_ping => sub {}, + irc_ctcp_time => sub {}, + irc_ctcpreply_ping => sub {}, + irc_ctcpreply_time => sub {}, + irc_invite => \&irc_invite, # INVITE to channel + irc_join => sub {}, + irc_kick => sub {}, + irc_kill => sub {}, + irc_mode => sub {}, + irc_nick => sub {}, + irc_part => sub {}, + irc_ping => sub {}, + irc_quit => sub {}, + irc_topic => sub {}, + irc_error => \&irc_error, + irc_disconnected => \&irc_disconnected, + irc_socketerr => \&irc_socketerr, + }, + args => [@ARGV]); + +$| = 1; +$poe_kernel->run(); +exit; + +# Core/bookkeeping test driver functions + +sub drv_start { + my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP]; + + # initialize heap + $heap->{clients} = {}; # session details, indexed by (short) session name + $heap->{sessions} = {}; # session details, indexed by session ref + $heap->{servers} = {}; # server addresses, indexed by short names + $heap->{macros} = {}; # macros + + # Parse arguments + foreach my $arg (@_[ARG0..$#_]) { + if ($arg =~ /^-D$/) { + $heap->{irc_debug} = 1; + } elsif ($arg =~ /^-V$/) { + $heap->{verbose} = 1; + } elsif ($arg =~ /^-vhost=(.*)$/) { + $heap->{vhost} = $1; + } else { + die "Extra command-line argument $arg\n" if $heap->{script}; + $heap->{script} = new FileHandle($arg, 'r') + or die "Unable to open $arg for reading: $!\n"; + } + } + die "No test name specified\n" unless $heap->{script}; + + # hook in to POE + $kernel->alias_set('control'); + $kernel->yield('heartbeat'); +} + +sub drv_heartbeat { + my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP]; + my $script = $heap->{script}; + my $used = {}; + my $delay = DELAY; + + while (1) { + my ($line, $lineno); + if ($heap->{line}) { + $line = delete $heap->{line}; + } elsif (defined($line = <$script>)) { + $heap->{lineno} = $.; + print "." unless $heap->{irc_debug}; + } else { + # close all connections + foreach my $client (values %{$heap->{clients}}) { + $kernel->call($client->{irc}, 'quit', "I fell off the end of my script"); + $client->{quitting} = 1; + } + # unalias the control session + $kernel->alias_remove('control'); + # die in a few seconds + $kernel->delay_set('die', 5); + return; + } + + chomp $line; + # ignore comments and blank lines + next if $line =~ /^\#/ or $line !~ /\S/; + + # expand any macros in the line + $line =~ s/(?<=[^\\])%(\S+?)%/$heap->{macros}->{$1} + or die "Use of undefined macro $1 at $heap->{lineno}\n"/eg; + # remove any \-escapes + $line =~ s/\\(.)/$1/g; + # figure out the type of line + if ($line =~ /^#/) { + # comment, silently ignore it + } elsif ($line =~ /^define (\S+) (.+)$/i) { + # define a new macro + $heap->{macros}->{$1} = $2; + } elsif ($line =~ /^undef (\S+)$/i) { + # remove the macro + delete $heap->{macros}->{$1}; + } elsif ($line =~ /^connect (\S+) (\S+) (\S+) (\S+) :(.+)$/i) { + # connect a new session (named $1) to server $4 + my ($name, $nick, $ident, $server, $userinfo, $port) = ($1, $2, $3, $4, $5, 6667); + $server = $heap->{servers}->{$server} || $server; + if ($server =~ /(.+):(\d+)/) { + $server = $1; + $port = $2; + } + die "Client with nick $nick already exists (line $heap->{lineno})" if $heap->{clients}->{$nick}; + my $alias = "client_$name"; + POE::Component::IRC->new($alias) + or die "Unable to create new user $nick (line $heap->{lineno}): $!"; + my $client = { name => $name, + nick => $nick, + ready => 0, + expect => [], + expect_alarms => [], + irc => $kernel->alias_resolve($alias), + params => { Nick => $nick, + Server => $server, + Port => $port, + Username => $ident, + Ircname => $userinfo, + Debug => $heap->{irc_debug}, + } + }; + $client->params->{LocalAddr} = $heap->{vhost} + if $heap->{vhost}; + $heap->{clients}->{$client->{name}} = $client; + $heap->{sessions}->{$client->{irc}} = $client; + $kernel->call($client->{irc}, 'register', 'all'); + $kernel->call($client->{irc}, 'connect', $client->{params}); + $used->{$name} = 1; + } elsif ($line =~ /^sync (.+)$/i) { + # do multi-way synchronization between every session named in $1 + my @synced = split(/,|\s/, $1); + # first, check that they exist and are ready + foreach my $clnt (@synced) { + die "Unknown session name $clnt (line $heap->{lineno})" unless $heap->{clients}->{$clnt}; + goto REDO unless $heap->{clients}->{$clnt}->{ready}; + } + # next we actually send the synchronization signals + foreach my $clnt (@synced) { + my $client = $heap->{clients}->{$clnt}; + $client->{sync_wait} = [map { $_ eq $clnt ? () : $heap->{clients}->{$_}->{nick} } @synced]; + $kernel->call($client->{irc}, 'notice', $client->{sync_wait}, 'SYNC'); + $kernel->call($session, 'disable_client', $client); + } + } elsif ($line =~ /^:(\S+) (\S+)(.*)$/i) { + # generic command handler + my ($names, $cmd, $args) = ($1, lc($2), $3); + my (@avail, @unavail); + # figure out whether each listed client is available or not + foreach my $c (split ',', $names) { + my $client = $heap->{clients}->{$c}; + if (not $client) { + print "ERROR: Unknown session name $c (line $heap->{lineno}; ignoring)\n"; + } elsif (($used->{$c} and not $zero_time->{$cmd}) or not $client->{ready}) { + push @unavail, $c; + } else { + push @avail, $c; + } + } + # redo command with unavailable clients + if (@unavail) { + # This will break if the command can cause a redo for + # available clients.. this should be fixed sometime + $line = ':'.join(',', @unavail).' '.$cmd.$args; + $heap->{redo} = 1; + } + # do command with available clients + if (@avail) { + # split up the argument part of the line + $args =~ /^((?:(?: [^:])|[^ ])+)?(?: :(.+))?$/; + $args = [($1 ? split(' ', $1) : ()), ($2 ? $2 : ())]; + # find the client and figure out if we need to wait + foreach my $c (@avail) { + my $client = $heap->{clients}->{$c}; + die "Client $c used twice as source (line $heap->{lineno})" if $used->{c} and not $zero_time->{$cmd}; + $kernel->call($session, 'cmd_'.$cmd, $client, $args); + $used->{$c} = 1 unless $zero_time->{$cmd}; + } + } + } else { + die "Unrecognized input line $heap->{lineno}: $line"; + } + if ($heap->{redo}) { + REDO: + delete $heap->{redo}; + $heap->{line} = $line; + last; + } + } + # issue new heartbeat with appropriate delay + $kernel->delay_set('heartbeat', $delay); +} + +sub drv_timeout_expect { + my ($kernel, $session, $client) = @_[KERNEL, SESSION, ARG0]; + print "ERROR: Dropping timed-out expectation by $client->{name}: ".join(',', @{$client->{expect}->[0]})."\n"; + $client->{expect_alarms}->[0] = undef; + unexpect($kernel, $session, $client); +} + +sub drv_reconnect { + my ($kernel, $session, $client) = @_[KERNEL, SESSION, ARG0]; + $kernel->call($client->{irc}, 'connect', $client->{params}); +} + +sub drv_default { + my ($kernel, $heap, $sender, $session, $state, $args) = @_[KERNEL, HEAP, SENDER, SESSION, ARG0, ARG1]; + if ($state =~ /^irc_(\d\d\d)$/) { + my $client = $heap->{sessions}->{$sender}; + if (@{$client->{expect}} + and $args->[0] eq $client->{expect}->[0]->[0] + and $client->{expect}->[0]->[1] eq "$1") { + my $expect = $client->{expect}->[0]; + my $mismatch; + for (my $x=2; ($x<=$#$expect) and ($x<=$#$args) and not $mismatch; $x++) { + $mismatch = 1 unless $args->[$x] =~ /$expect->[$x]/i; + } + unexpect($kernel, $session, $client) unless $mismatch; + } + return undef; + } + print "ERROR: Unexpected event $state to test driver (from ".$sender->ID.")\n"; + return undef; +} + +# client-based command issuers + +sub cmd_message { + my ($kernel, $heap, $event, $client, $args) = @_[KERNEL, HEAP, STATE, ARG0, ARG1]; + die "Missing arguments" unless $#$args >= 1; + # translate each target as appropriate (e.g. *sessionname) + my @targets = split(/,/, $args->[0]); + foreach my $target (@targets) { + if ($target =~ /^\*(.+)$/) { + my $other = $heap->{clients}->{$1} or die "Unknown session name $1 (line $heap->{lineno})\n"; + $target = $other->{nick}; + } + } + $kernel->call($client->{irc}, substr($event, 4), \@targets, $args->[1]); +} + +sub cmd_generic { + my ($kernel, $heap, $event, $client, $args) = @_[KERNEL, HEAP, STATE, ARG0, ARG1]; + $event =~ s/^cmd_//; + $kernel->call($client->{irc}, $event, @$args); +} + +sub cmd_raw { + my ($kernel, $heap, $client, $args) = @_[KERNEL, HEAP, ARG0, ARG1]; + die "Missing argument" unless $#$args >= 0; + $kernel->call($client->{irc}, 'sl', $args->[0]); +} + +sub cmd_sleep { + my ($kernel, $session, $heap, $client, $args) = @_[KERNEL, SESSION, HEAP, ARG0, ARG1]; + die "Missing argument" unless $#$args >= 0; + $kernel->call($session, 'disable_client', $client); + $kernel->delay_set('enable_client', $args->[0], $client); +} + +sub cmd_wait { + my ($kernel, $session, $heap, $client, $args) = @_[KERNEL, SESSION, HEAP, ARG0, ARG1]; + die "Missing argument" unless $#$args >= 0; + # if argument was comma-delimited, split it up (space-delimited is split by generic parser) + $args = [split(/,/, $args->[0])] if $args->[0] =~ /,/; + # make sure we only wait if all the other clients are ready + foreach my $other (@$args) { + if (not $heap->{clients}->{$other}->{ready}) { + $heap->{redo} = 1; + return; + } + } + # disable this client, make the others send SYNC to it + $kernel->call($session, 'disable_client', $client); + $client->{sync_wait} = [map { $heap->{clients}->{$_}->{nick} } @$args]; + foreach my $other (@$args) { + die "Cannot wait on self" if $other eq $client->{name}; + $kernel->call($heap->{clients}->{$other}->{irc}, 'notice', $client->{nick}, 'SYNC'); + } +} + +sub cmd_expect { + my ($kernel, $session, $heap, $client, $args) = @_[KERNEL, SESSION, HEAP, ARG0, ARG1]; + die "Missing argument" unless $#$args >= 0; + push @{$client->{expect}}, $args; + push @{$client->{expect_alarms}}, $kernel->delay_set('timeout_expect', EXPECT_TIMEOUT, $client); + $kernel->call($session, 'disable_client', $client); +} + +# handlers for messages from IRC + +sub unexpect { + my ($kernel, $session, $client) = @_; + shift @{$client->{expect}}; + my $alarm_id = shift @{$client->{expect_alarms}}; + $kernel->alarm_remove($alarm_id) if $alarm_id; + $kernel->call($session, 'enable_client', $client) unless @{$client->{expect}}; +} + +sub check_expect { + my ($kernel, $session, $heap, $poe_sender, $sender, $text) = @_[KERNEL, SESSION, HEAP, SENDER, ARG0, ARG1]; + my $client = $heap->{sessions}->{$poe_sender}; + my $expected = $client->{expect}->[0]; + + # check sender + if ($expected->[0] =~ /\*(.+)/) { + # we expect *sessionname, so look up session's current nick + my $exp = $1; + $sender =~ /^(.+)!/; + return 0 if lc($heap->{clients}->{$exp}->{nick}) ne lc($1); + } elsif ($expected->[0] =~ /^:?(.+!.+)/) { + # expect :nick!user@host, so compare whole thing + return 0 if lc($1) ne lc($sender); + } else { + # we only expect :nick, so compare that part + $sender =~ /^:?(.+)!/; + return 0 if lc($expected->[0]) ne lc($1); + } + + # compare text + return 0 if lc($text) !~ /$expected->[2]/i; + + # drop expectation of event + unexpect($kernel, $session, $client); +} + +sub irc_connected { + my ($kernel, $session, $heap, $sender) = @_[KERNEL, SESSION, HEAP, SENDER]; + my $client = $heap->{sessions}->{$sender}; + print "Client $client->{name} connected to server $_[ARG0]\n" if $heap->{verbose}; + $kernel->call($session, 'enable_client', $client); +} + +sub irc_disconnected { + my ($kernel, $session, $heap, $sender, $server) = @_[KERNEL, SESSION, HEAP, SENDER, ARG0]; + my $client = $heap->{sessions}->{$sender}; + print "Client $client->{name} disconnected from server $_[ARG0]\n" if $heap->{verbose}; + if ($client->{quitting}) { + $kernel->call($sender, 'unregister', 'all'); + delete $heap->{sessions}->{$sender}; + delete $heap->{clients}->{$client->{name}}; + } else { + if ($client->{disconnect_expected}) { + delete $client->{disconnect_expected}; + } else { + print "Got unexpected disconnect for $client->{name} (nick $client->{nick})\n"; + } + $kernel->call($session, 'disable_client', $client); + $kernel->delay_set('reconnect', $client->{throttled} ? THROTTLED_TIMEOUT : RECONNECT_TIMEOUT, $client); + delete $client->{throttled}; + } +} + +sub irc_socketerr { + my ($kernel, $session, $heap, $sender, $msg) = @_[KERNEL, SESSION, HEAP, SENDER, ARG0]; + my $client = $heap->{sessions}->{$sender}; + print "Client $client->{name} (re-)connect error: $_[ARG0]\n"; + if ($client->{quitting}) { + $kernel->call($sender, 'unregister', 'all'); + delete $heap->{sessions}->{$sender}; + delete $heap->{clients}->{$client->{name}}; + } else { + if ($client->{disconnect_expected}) { + delete $client->{disconnect_expected}; + } else { + print "Got unexpected disconnect for $client->{name} (nick $client->{nick})\n"; + } + $kernel->call($session, 'disable_client', $client); + $kernel->delay_set('reconnect', $client->{throttled} ? THROTTLED_TIMEOUT : RECONNECT_TIMEOUT, $client); + delete $client->{throttled}; + } +} + +sub irc_notice { + my ($kernel, $session, $heap, $sender, $from, $to, $text) = @_[KERNEL, SESSION, HEAP, SENDER, ARG0, ARG1, ARG2]; + my $client = $heap->{sessions}->{$sender}; + if ($client->{sync_wait} and $text eq 'SYNC') { + $from =~ s/!.+$//; + my $x; + # find who sent it.. + for ($x=0; $x<=$#{$client->{sync_wait}}; $x++) { + last if $from eq $client->{sync_wait}->[$x]; + } + # exit if we don't expect them + if ($x>$#{$client->{sync_wait}}) { + print "Got unexpected SYNC from $from to $client->{name} ($client->{nick})\n"; + return; + } + # remove from the list of people we're waiting for + splice @{$client->{sync_wait}}, $x, 1; + # re-enable client if we're done waiting + if ($#{$client->{sync_wait}} == -1) { + delete $client->{sync_wait}; + $kernel->call($session, 'enable_client', $client); + } + } elsif (@{$client->{expect}} + and $client->{expect}->[0]->[1] =~ /notice/i) { + check_expect(@_[0..ARG0], $text); + } +} + +sub irc_msg { + my ($kernel, $session, $heap, $sender, $from, $to, $text) = @_[KERNEL, SESSION, HEAP, SENDER, ARG0, ARG1, ARG2]; + my $client = $heap->{sessions}->{$sender}; + if (@{$client->{expect}} + and $client->{expect}->[0]->[1] =~ /msg/i) { + check_expect(@_[0..ARG0], $text); + } +} + +sub irc_public { + my ($kernel, $session, $heap, $sender, $from, $to, $text) = @_[KERNEL, SESSION, HEAP, SENDER, ARG0, ARG1, ARG2]; + my $client = $heap->{sessions}->{$sender}; + if (@{$client->{expect}} + and $client->{expect}->[0]->[1] =~ /public/i + and grep($client->{expect}->[0]->[2], @$to)) { + splice @{$client->{expect}->[0]}, 2, 1; + check_expect(@_[0..ARG0], $text); + } +} + +sub irc_invite { + my ($kernel, $session, $heap, $sender, $from, $to) = @_[KERNEL, SESSION, HEAP, SENDER, ARG0, ARG1, ARG2]; + my $client = $heap->{sessions}->{$sender}; + if (ref $client->{expect} eq 'ARRAY' + and $client->{expect}->[0]->[1] =~ /invite/i + and $to =~ /$client->{expect}->[0]->[2]/) { + check_expect(@_[0..ARG0], $to); + } +} + +sub irc_error { + my ($kernel, $session, $heap, $sender, $what) = @_[KERNEL, SESSION, HEAP, SENDER, ARG0]; + my $client = $heap->{sessions}->{$sender}; + if (@{$client->{expect}} + and $client->{expect}->[0]->[1] =~ /error/i) { + splice @{$client->{expect}->[0]}, 2, 1; + unexpect($kernel, $session, $client); + $client->{disconnect_expected} = 1; + } else { + print "ERROR: From server to $client->{name}: $what\n"; + } + $client->{throttled} = 1 if $what =~ /throttled/i; +} ----------------------- End of diff ----------------------- |