#!%PERL%

#    =====================================================================
#    |                   SPALEWARE LICENSE (Revision 0.1)                |
#    |-------------------------------------------------------------------|
#    | This file is part of a package called "GenEthic" and is           |
#    | licensed under SPALEWARE. You may freely modify and distribute    |
#    | this package or parts of it. But you MUST keep the SPALWARE       |
#    | license in it!                                                    |
#    |                                                                   |
#    =====================================================================
#
#

## Name     : GenEthic
## Version  : 1.2.1
## Author   : Pascal Gloor
## Contact  : spale@undernet.org
## Date     : 7 Sept 2003

use IO::Socket;

# loading configuration file into variables

$|=1;
$path = $ARGV[0];

load_conf();

# testing if TCP connection is possible

$ircserver = IO::Socket::INET->new(
		PeerAddr   => $C{'CONN_DST_IP'},
		PeerPort   => $C{'CONN_DST_PORT'},
		Proto      => 'tcp',
		LocalAddr  => $C{'CONN_SRC_IP'}  ) || die "Check your config, TCP connection not possible";

# setting some time vars
$time{'old1'} = time;
$time{'old2'} = time;
$time{'old3'} = time;
$time{'old4'} = time;
$time{'old5'} = time;
$time{'init'} = time;

# connecting to IRC Server and entering in main loop

while(1) {
	vec($wdata,fileno($ircserver),1) = 1;
	if ( select($wdata, undef, undef, 0.1) ) {
		$line = <$ircserver>;
		$line =~ s/(\r|\n)+$//;
	} else {
		undef $line;
	}

# getting time for this loop
	$time{'log'} = time;

if ( $line ) {

	if ( $line =~ /^ERROR :Closing Link/ ) {
		print STDERR "GenEthic $C{'VERSION'}: $line\n";
		shutdown($ircserver, 2);
		exit;
	}

	if($C{'DEBUG'}) {
		$noprint = 0;
		if ( $C{'DEBUG_HIDE_WHO'} ) {
			if ( $line =~ / 354 / ) { $noprint = 1; }
			if ( $line =~ / 211 / ) { $noprint = 1; }
		}

		if ( $C{'DEBUG_HIDE_CONNECT'} ) {
			if ( $line =~ / client connecting: /i ) { $noprint = 1; }
			elsif ( $line =~ / client exiting: /i ) { $noprint = 1; }
		}

		if ( !$noprint ) { debug("IN  $line"); }
	}


# some special chars allowed in nicknames and text are substituted by others
# to be sure nothing bad can be done...

	$line =~ s/\[/\#\#20/g;
	$line =~ s/\]/\#\#21/g;
	$line =~ s/\|/\#\#22/g;
	$line =~ s/\^/\#\#23/g;
	$line =~ s/\`/\#\#24/g;
	$line =~ s/\'/\#\#25/g;
	$line =~ s/\~/\#\#26/g;
	$line =~ s/\\/\#\#27/g;
	$line =~ s/\{/\#\#28/g;
	$line =~ s/\}/\#\#29/g;

# keep 'keepalive' file updated

	open(KEEPALIVE,">$path/tmp/keepalive");
	print KEEPALIVE $time{'log'} . "\n";
	close(KEEPALIVE);

# stuff needed to connect

	if ( !$first ) {
		if ( $C{'USER_PASS'} ne "" ) { quicksend("PASS $C{'USER_PASS'}"); }
		quicksend("USER $C{'USER_IDENT'} not_used not_used :$C{'USER_RNAME'}");
		quicksend("NICK $botnick");
		$first = "connected";
	}

# taking care to reply to PINGS

	if ( $line =~ /^PING / ) {
		$ping = $line;
		$ping =~ s/^PING /PONG /;
		quicksend("$ping");
	}


# trying to handle all types of incomming messages :-P
# sort them, put them in vars,etc etc....

	$from=''; $type=''; $type2=''; $content=''; $nick='';$user='';$host='';$fullhost='';$msgsrc='';
	if ( $line =~ /^:/ ) {
		$line =~ s/^://g;
		$from = $line; $from =~ s/ .*//g;
		$type  = $line; $type =~ s/^$from //g; $type =~ s/ .*//g;
		$type2 = $line; $type2 =~ s/^$from $type //g; $type2 =~ s/ .*//g;
		if ( $type2 =~ /^:/ ) { undef $type2; }
		$content = $line; $content =~ s/^$from $type $type2.//g; $content =~ s/^://g;

		if ( $from =~ /\@/ && $from =~ /\!/ ) {
			$msgsrc = "user";
			($nick,$fullhost)=split(/\!/,$from);
			($user,$host)=split(/\@/,$fullhost);
		} else {
			$msgsrc = "server";
			$nick = "";
			$user = "";
			$host = "";
		}
	}

# lets check if you're not under floodbots attack :-) or any other mass (dis)connects...

	if ( $locusers ne "" && $oldlocusers eq "" ) { $oldlocusers = $locusers; $time{'lastusers'} = $time{'log'}; }

	$usersdiff = $locusers - $oldlocusers;
	$tdiff = $time{'log'} - $time{'lastusers'};
	if ( abs($usersdiff) >= $C{'USER_THRESHOLD'} && $tdiff >= $C{'TIME_THRESHOLD'} ) {
	        if ( $usersdiff =~ /^[0-9]/ ) { $usersdiff = "+" . $usersdiff; }
	        if ( abs($usersdiff/$tdiff)*100 >= $C{'USER_SEC_THRESHOLD'} ) {
			ircsend("NOTICE $C{'CHAN_NAME'} :WARNING Possible attack: $usersdiff (+$usersmore1/-$usersless1) users in $tdiff seconds ($locusers users)");
	        } elsif ( $C{'PRINT_ONLY_WARNING'} ne "TRUE" ) {
	                ircsend("NOTICE $C{'CHAN_NAME'} :$usersdiff (+$usersmore1/-$usersless1) users in $tdiff seconds ($locusers users)");
	        }
	        $oldlocusers = $locusers;
	        $time{'lastusers'} = $time{'log'};
		$usersmore1 = 0;
		$usersless1 = 0;
	}


# ok now, the big part, taking care of what to do with all this information....

	if ( $msgsrc eq "server" ) {
		# nickname already used, changing to next one
		if ( $type eq "433" ) {
			$botnick = shift(@NICKS);
			if ( $botnick eq '' ) {
				print STDERR "Fatal Error, no more nicknames available, change your config!\n";
				exit 1;
			}
#			print "changing nick to $botnick because: $line\n";
			quicksend("NICK $botnick");
		# Hey, i m connected now, lets do some stuff now..
		} elsif ( $type eq "001" ) {
			if ( $C{'X_AUTH'} eq "TRUE" ) {
				ircsend("PRIVMSG $C{'X_HOST'} :login $C{'X_USER'} $C{'X_PASS'}");
			}
			ircsend("OPER $C{'O_USER'} :$C{'O_PASS'}");
			ircsend("MODE $botnick +idx");
			ircsend("MODE $botnick +s 32767");
			ircsend("JOIN $C{'CHAN_NAME'}");
			ircsend("JOIN $C{'CHAN_NAME'} :OVERRIDE");
			ircsend("MODE $C{'CHAN_NAME'} +o $botnick");
			if (  $C{'CHAN_KEEP_MODES'} eq "TRUE" ) {
				ircsend("MODE $C{'CHAN_NAME'} $C{'CHAN_MODES'}");
			}
		# STATS C Result
		} elsif ( $type eq "213" ) {
			($null,$null,$null,$uplink,$null,$null)=split(/ /,$content);
			$uplink =~ tr/[A-Z]/[a-z]/;
			push(@HUBS,"$uplink");
			$havehubs = "yes";

		# End of stats C
		} elsif ( $type eq "219" && $havehubs eq "yes" ) {
			foreach(@HUBS) {
				ircsend("RPING $_ :12345678");
			}
			$havehubs = "no";
		# receiving WHO result, and working on it... fucking hard...
		} elsif ( $type eq "354" ) {
			($cuser,$cip,$chost,$cnick,$cidle)=split(/ /,$content);
			$crname = $content;
			$crname =~ s/^$cuser $cip $chost $cnick $cidle ://;
			open(FILE,">>$path/tmp/who.txt");
			print FILE "$cnick	$cuser	$chost	$cip	$cidle	$crname\n";
			close(FILE);

		# end of WHO, searching for clones..
		} elsif ( $type eq "315" ) {
			foreach(split(/\,/,$C{'RNAME_WHITE_LIST'})) { push(@WL,$_); }
			undef %wrname;
			undef @WRNAME;
			open(FILE,"$path/tmp/who.txt");
			while(<FILE>) {
				chop;
				($wnick,$wuser,$whost,$wip,$widle,$wrname)=split(/	/);
				foreach(@WL) { if ( $wrname =~ /^$_$/ ) { undef $wrname; } }
				if ( $wrname =~ /./ ) {
					$wrname{$_}++;
					push(@WRNAME,$_);
				}
			} close(FILE);
			undef @WRNAME2;
			foreach $wrname (sort(@WRNAME)) {
				if ( $wrname ne $lastwr ) {
					$wrname{$wrname} =~ sprintf("%05d",$wrname{$wrname});
					push(@WRNAME2,"$wrname{$wrname} $wrname");
				}
				$lastwr = $wrname;
			}
			foreach (@WRNAME2) {
				$countwr = $_;
				$countwr =~ s/ .*//;
				$wrname = $_;
				$wrname =~ s/^$countwr //;

				if ( $countwr >= $C{'RNAME_LIMIT'} ) {
					if($C{'DEBUG'}>1) { debug("DEBUG: GLINE RealName \"$wrname\" $countwr hosts found"); }
					clientsend("NOTICE $C{'CHAN_NAME'} :Auto Gline $countwr hosts with Realname \"$wrname\"");
					undef @TOGLINE;
					open(FILE,"$path/tmp/who.txt");
					while(<FILE>) {
						chop;
						($wnick,$wuser,$whost,$wip,$widle,$wrname2)=split(/	/);
						if ( $wrname eq $wrname2 ) {
							push(@TOGLINE,"$wuser\@$wip");
						}
					} close(FILE);
					foreach(sort(@TOGLINE)) {
						if ( $_ ne $lastrhost ) {
							clientsend("GLINE +$_ $C{'RNAME_GLINE_TIME'} :auto gline"); 
						}	
						$lastrhost = $_;
					}
					clientsend("NOTICE $C{'CHAN_NAME'} :Auto Gline \"$lastrname\" done.");
					undef $lastrhost;
				}
			}

		# getting list of users when entering in channel, whois'ing them
		} elsif (  $type eq "353" ) {
			ircsend("NOTICE $C{'CHAN_NAME'} :GenEthic Bot $C{'VERSION'}, (init) initialisation, please wait ...");
			$initstart=1;
			$content =~ s/.*://g;
			$content =~ s/\@//g;
			foreach(split(/ /,$content)) { ircsend("WHOIS $_"); }
		# hey, i m sure this guy  is an oper
		} elsif ( $type eq "313" ) {
			if ( $content =~ /is an IRC Operator/ ) {
				$content =~ s/ .*//;
				$opers{$content} = 'yes';
			}
		# end of whois...humm.. perhaps an action to take
		} elsif ( $type eq "318" ) {
			$content  =~ s/ :.*//;
			if ( $C{'CHAN_OPER_ONLY'} eq "TRUE" ) {
				if ( $opers{$content} eq "yes" ) {
					ircsend("MODE $C{'CHAN_NAME'} +o $content");
				} else {
					if ( $C{'CHAN_OPER_ACTION'} eq "KICK" ) {
						ircsend("KICK $C{'CHAN_NAME'} $content :$C{'CHAN_OPER_ACTION_REASON'}");
					} elsif ( $C{'CHAN_OPER_ACTION'} eq "KILL" ) {
						ircsend("KILL $content :$C{'CHAN_OPER_ACTION_REASON'}");
					}	
				}
			} else {
				ircsend("MODE $C{'CHAN_NAME'} -o $content");
				ircsend("MODE $C{'CHAN_NAME'} -v $content");
			}
		# Humm, i m receiving a MAP result, lets work on that
		} elsif ( $type eq "015" ) {
			$mapserver = $content;
			$mapserver =~ s/.*\-//;
			$mapserver =~ s/ .*//;
			$mapclient = $content;
			$mapclient =~ s/.* \#\#20/00000/;
			$mapclient =~ s/ .*//;
			$mapclient =~ s/^.*(......)$/$1/;
			if ( $mapclient >= $C{'MAP_THRESHOLD'} ) {
				push(@MAP,"$mapclient:$mapserver");
			}
		# STATS L result
		} elsif ( $type eq "211" ) {

			if ( $content =~ /$C{'HUB_COMMON'} .*:[0-9]+$/i ) {
				$hub_name = $hub_link = $content;
				$hub_link =~ s/.*://;
				$hub_link = time - $hub_link;
				$hub_name =~ s/$C{'HUB_COMMON'} .*$//i;
				$hub_name =~ tr/[A-Z]/[a-z]/;

#			} elsif ( $content =~ /:[0-9]+$/ ) {
#				
#				$top_time = $top_nick = $content;
#				$top_time =~ s/.*://;
#				$top_nick =~ s/ .*//;
#
#				if ( $top_time > $best_top_time ) {
#					$best_top_time = $top_time;
#					$best_top_nick = $top_nick;
#				}
			}
			
		# LUSERS result
		} elsif ( $type eq "251" ) {
			($null,$null,$user1,$null,$null,$user2,$null,$null,$servers,$null)=split(/ /,$content);
			$users = $user1 + $user2;
		# get ircd uptime
		} elsif ( $type eq "242" ) {
			$ircduptime = $content;
			$ircduptime =~ s/^Server Up //;
		# get number of channels from LUSERS
		} elsif ( $type eq "254" ) {
			$channels = $content;
			$channels =~ s/ .*//;
		# get number of local users from LUSERS
		} elsif ( $type eq "255" ) {
			$locusers = $content;
			$locusers =~ s/I have (.*) clients.*/$1/;
		# RPONG RESULT
		} elsif ( $type eq "RPONG" ) {
			($rpongserv,$rpongtime)=split(/ /,$content);
			$rpongserv =~ tr/[A-Z]/[a-z]/;
			$rping{$rpongserv} = $rpongtime;
		# channel modes changes
		} elsif ( $type eq "MODE" && $type2 =~ /^$C{'CHAN_NAME'}$/i ) {
			ircsend("MODE $C{'CHAN_NAME'} $C{'CHAN_MODES'}");
		# and now some server notices...
		} elsif ( $type eq "NOTICE" ) {
			# max users..
			if ( $content =~ /^Highest connection count/ ) {
				$maxusers = $content;
				$maxusers =~ s/.*\((.*) clients\)/$1/;
				if ( $maxusers > $oldmaxusers ) {
					ircsend("MODE $C{'CHAN_NAME'} +l $maxusers");
				}
				$oldmaxusers = $maxusers;
			# connecting users
			} elsif ( $content =~ /\*\*\* Notice -- Client connecting: / ) {
				$locusers++;
				$usersmore1++;
				$usersmore2++;

			# disconnecting users
			} elsif ( $content =~ /\*\*\* Notice -- Client exiting: / ) {
				$locusers--;
				$usersless1++;
				$usersless2++;
			} elsif ( $content =~ /\*\*\* Notice -- Failed OPER attempt by / && $C{'OPER_FAIL_CHECK'} eq "TRUE" ) {
				$content =~ s/.*Failed OPER attempt by //;
				$content =~ s/\(|\)//g;
				($opernick,$operhost)=split(/ /,$content);
				$operfail{$operhost}++;
				if ( $C{'OPER_FAIL_MAX'} <= $operfail{$operhost} ) {
					if ( $C{'OPER_FAIL_ACTION'} eq "KILL" ) {
						ircsend("KILL $opernick :$C{'OPER_FAIL_ACTION_REASON'}");
					} elsif ( $C{'OPER_FAIL_ACTION'} eq "LGLINE" ) {
						ircsend("GLINE +$operhost $C{'OPER_FAIL_GLINE_TIME'} :$C{'OPER_FAIL_ACTION_REASON'}");
					}
					ircsend("NOTICE $C{'CHAN_NAME'} :OPER Fail $opernick\!$operhost (count:$operfail{$operhost}, $C{'OPER_FAIL_ACTION'})");
					$operfail{$operhost}=0;
				} else {
					ircsend("NOTICE $opernick :$C{'OPER_FAIL_WARNING'}");
					ircsend("NOTICE $C{'CHAN_NAME'} :OPER Fail $opernick\!$operhost (count:$operfail{$operhost})");
				}
			} elsif ( $content =~ /\*\*\* Notice -- Net break: / ) {
				$content =~ s/.*\*\*\* Notice -- Net break: //;
				ircsend("NOTICE $C{'CHAN_NAME'} :SPLIT: $content");
			} elsif ( $content =~ /\*\*\* Notice -- Net junction: / ) {
				$content =~ s/.*\*\*\* Notice -- Net junction: //;
				ircsend("NOTICE $C{'CHAN_NAME'} :JOIN: $content");
			} elsif ( $content =~ /\*\*\* Notice -- HACK\(4\): / ) {
				$content =~ s/.*\*\*\* Notice -- HACK\(4\): //;
				$hackcount++;
				if ( $hackcount > 100 ) {
					$hackcount = 1;
					open(HACKFILE,"$path/var/hack4.log");
					while(<HACKFILE>) {
						$hacktime = $_;
						chop $hacktime;
						$hacktime =~ s/ .*//;
						if ( $hacktime < (time-$C{'HACK4_EXPIRE'}) ) {
							push(@HACK,$_);
						}
					} close(HACKFILE);
					open(HACKFILE,">$path/var/hack4.log");
					foreach(@HACK) { print HACKFILE $_; }
					close(HACKFILE);
					undef @HACK;
				}
				open(HACKFILE,">>$path/var/hack4.log");
				print HACKFILE time . " $content\n";
				close(HACKFILE);
			} elsif ( $C{'LOG_MISDNS'} eq "TRUE" && $content =~ /\*\*\* Notice -- IP\# Mismatch: / ) {
				$content =~ s/.*IP\# Mismatch: //;
				$content =~ s/\#\#20/ /; $content =~ s/\#\#21//;
				$content =~ s/\!\= //;
				open(DNSLOG,">>$C{'LOGFILE_MISDNS'}");
				print DNSLOG "$content\n";
				close(DNSLOG);

			} elsif (  $content =~ / is now operator / ) {
				$content =~ s/.*Notice -- //;
				($opernick,$operhost,$null,$null,$null,$opertype)=split(/ /,$content);
				$operhost =~ s/\(|\)//g;
				if ( $opertype eq "(o)" ) { $opertype = "LocalOPER"; } else { $opertype = "GlobalOPER"; }
				ircsend("NOTICE $C{'CHAN_NAME'} :$opernick\!$operhost is now $opertype");
				if ( $C{'CHAN_OPER_INV'} eq 'TRUE' ) {
					ircsend("INVITE $opernick $C{'CHAN_NAME'}");
				}

			}
		}
	} elsif ( $msgsrc eq "user" && $nick ne $botnick ) {
		# hey, a guy is joining, but who is he?
		if ( $type eq "JOIN" ) {
			ircsend("WHOIS $nick");
			if ( $C{'X_AUTH'} eq "TRUE" ) {
				ircsend("PRIVMSG $C{'X_NICK'} :VERIFY $nick");
			}
		#  that guy is leaving, unauthing...
		} elsif ( $type eq "PART" || $type eq "QUIT" ) {
			$opers{$nick} = '';
		# Detecting authenticated users nick change.
		} elsif ( $type eq "NICK" ) {
			if ( $opers{$nick} eq "yes" ) {
				$opers{$nick} = '';
				$opers{$content} = "yes";
#				ircsend("NOTICE $C{'CHAN_NAME'} :Nick change detected, $nick -> $content");
			}
		# CSBot has information for me ;-) lets share that...
		} elsif (  $type eq "NOTICE" && $nick eq "$C{'X_NICK'}" ) {
			if ( $content =~ /^Remember:/ ) { $ignore; } else {
				ircsend("NOTICE $C{'CHAN_NAME'} :From $nick: $content");
			}
		# yes, a guy is talking to me ;p
		} elsif (( $type eq "PRIVMSG" || $type eq "NOTICE" ) && $opers{$nick} eq "yes" ) {
			# lets manage common CTCP replies
			if ( $content =~ // ) {
				$content =~ s/^.(.*).$/$1/;
				if (  $content =~ /^PING / ) {
					quicksend("NOTICE $nick :$content");
				} elsif ( $content =~ /^VERSION/ && $C{'VERSION'} ne '' ) {
	                        	ircsend("NOTICE $nick :VERSION $C{'VERSION'}");
	                	} elsif ( $content =~ /^TIME/ ) {
	                        	ircsend("NOTICE $nick :TIME $time{'log'}");
	                	} elsif ( $content =~ /^FINGER/ && $C{'FINGER'} ne '' ) {
	                        	ircsend("NOTICE $nick :FINGER $C{'FINGER'}");
	                	}

			# lets give him sime help as requested..
			} elsif (  $content =~ /^help$/i ) {
				$totalhelp ='';
				foreach(@HELP) {
					($hindex,$hdetail)=split(/\;/);
					$totalhelp = "$totalhelp $hindex";
				}
				$totalhelp =~ s/^ //;
				clientsend("NOTICE $nick :HELP INDEX: $totalhelp");
				clientsend("NOTICE $nick :Try /notice $botnick help <command>");
			# or detailed help
			} elsif (  $content =~ /^help /i ) {
				foreach(@HELP) {
					($hindex,$hdetail)=split(/\;/);
					if ( $content =~ /^help $hindex$/i || $content =~ /^help all$/i ) {
						$hdetail =~ s/^ *//;
						clientsend("NOTICE $nick :$hdetail");
					}
				}
			# the user want to reload my config
			} elsif ( $content =~ /^reload$/i ) {
				load_conf();
				clientsend("NOTICE $nick :Config files reloaded...");
			# the user want me to change nickname
			} elsif (  $content =~ /^nick /i ) {
				$content =~ s/^nick //i;
				ircsend("NICK $content");
			# let the user send some RAW commands
			# can be userfull for debugging, or others?
			} elsif ( $content =~ /^raw /i ) {
				$content =~ s/^raw //i;
				ircsend("$content");
			# wanna talk  in the channel?
			} elsif ( $content =~ /^chan /i ) {
				$content =~ s/^chan //i;
				ircsend("PRIVMSG $C{'CHAN_NAME'} :$content");
			# how to suicide ;)
			} elsif ( $content =~ /^quit /i ) {
				$content =~ s/^quit /quit :/i;
				ircsend("NOTICE $C{'CHAN_NAME'} :Quitting, requested by $nick");
				ircsend("$content");
			# humm... showing the map can be huge, the user is the king, let do.
			} elsif ( $content =~ /^map$/i ) {
				clientsend("NOTICE $nick :Network MAP ordered by users");
				$counter = 0;
				foreach(reverse(sort(@MAP))) {
					($mapuser,$mapserv)=split(/:/);
					$counter++;
					$counter = "00$counter"; $counter =~ s/.*(..)$/$1/;
					if ( $mapserv =~ /^$C{'SERVER_NAME'}$/i ) {
						clientsend("NOTICE $nick :$counter($mapuser) $mapserv");
					} else {
						clientsend("NOTICE $nick :$counter($mapuser) $mapserv");
					}
				}
				clientsend("NOTICE $nick :End of MAP");
			} elsif ( $content =~ /^scan/i ) {
				$content =~ s/^scan +//i;

				if ( $content =~ /^.*\@.*$/ ) {
					($suser,$shost)=split(/\@/,$content);
					$suser  =~ s/\*/\.\*/g;
					$shost  =~ s/\*/\.\*/g;
					$scount = '0';

					open(FILE,"$path/tmp/who.txt");
					while(<FILE>) {
						chop;
						($wnick,$wuser,$whost,$wip,$widle,$wrname)=split(/	/);
						if ( $wuser =~ /^$suser$/i && ( $wip =~ /^$shost$/i || $whost =~ /^$shost$/i ) ) {
							$scount++;
						}
					} close(FILE);
					clientsend("NOTICE $nick :Scanned ($content), $scount users found.");

				} elsif ( $content =~ /^.+$/ ) {
					$ssearch = $content;
					$ssearch =~ s/\*/\.\*/g;
					$scount = '0';
					open(FILE,"$path/tmp/who.txt");
					while(<FILE>) {
						chop;
						($wnick,$wuser,$whost,$wip,$widle,$wrname)=split(/	/);
						if ( $wrname =~ /^$ssearch$/i ) { $scount++; }
					} close(FILE);
					clientsend("NOTICE $nick :Scanned rname ($content), $scount users found.");

				} else {
					clientsend("NOTICE $nick :syntax error, scan <user@host> or scan <rname> (* allowed)");
				}

			# checking for HACK4 messages
			} elsif ( $content =~ /^hack/i ) {
				$content =~ s/^hack +//i;
				if ( $content ) {
					open(HACK,"$path/var/hack4.log");
					while(<HACK>) {
						s/(\r|\n)+$//;
						if ( /\Q$content/i ) {
							push(@USERHACK,$_);
						}
					}
					undef $only5;
					clientsend("NOTICE $nick :HACK4 message search for \"$content\"");
					foreach(reverse(@USERHACK)) {
						$only5++;
						if ( $only5 <= 5 ) {
							clientsend("NOTICE $nick :$_");
						}
					}
					undef @USERHACK;
					if ( !$only5 ) {
						clientsend("NOTICE $nick :nothing found.");
					}
				} else {
					clientsend("NOTICE $nick :syntax error, hack <string to search>");
				}

			# checking for clones
			} elsif ( $content =~ /^clones/i ) {
				$content =~ s/^clones //i;
				if ( $content =~ /^(nick|user|host|ip|rname|idle)$/i ) {
					$field = $content;
					$field =~ tr/[A-Z]/[a-z]/;
					$disfield = $field;
					$field =~ s/^/w/;
					$ccount = 0;
					undef @WFIELD;
					undef %wcount;
					open(FILE,"$path/tmp/who.txt");
					while(<FILE>) {
						chop;
						($wnick,$wuser,$whost,$wip,$widle,$wrname)=split(/	/);

						$wip =~ s/\.[0-9]+$/\.\*/;

						if    ( $widle < 3600     ) { $widle = '< 1 hour'; }
						elsif ( $widle < 86400    ) { $widle = '< 1 day'; }
						elsif ( $widle < 604800   ) { $widle = '< 1 week'; }
						elsif ( $widle < 18144000 ) { $widle = '< 1 month'; }
						else                        { $widle = '> 1 month'; }

						if ( $ctld =~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ ) {
							$ctld =~ s/\.[0-9]+$/\.\*/;
						} else {
							$ctld = $cdom = $whost;
							$ctld =~ s/.*\.//g;
							$cdom =~ s/$ctld$//;
							if ( $cdom =~ /\.$/ ) {
								$cdom =~ s/\.$//;
								if ( $cdom =~ /\./ ) {
									$cdom =~ s/.*\.//g;
									$whost = "*.$cdom.$ctld";
								} else {
									$whost = "$cdom.$ctld";
								}
							} else {
								$whost ="$cdom";
							}
						}

						$wcount{$$field}++;
						push(@WFIELD,$$field);
					} close(FILE);
					undef @WFIELD2;
					foreach $wfield (sort(@WFIELD)) {
						if ( $wlast ne $wfield ) {
							$wcount{$wfield} = sprintf("%05d",$wcount{$wfield});
							push(@WFIELD2,"$wcount{$wfield} $wfield");
						}
						$wlast = $wfield;
					}
					clientsend("NOTICE $C{'CHAN_NAME'} :clones/botnets detection on field \'$disfield\' (requested by: $nick)");
					undef $loopcount;
					foreach (reverse(sort(@WFIELD2))) {
						$loopcount++;
						if ( $loopcount <= 5 ) {
							$wcount = $_;
							$wcount =~ s/ .*//;
							$wfield = $_;
							$wfield =~ s/^$wcount //;
							clientsend("NOTICE $C{'CHAN_NAME'} :found $wcount users \"$wfield\"");
						}
					}
					clientsend("NOTICE $C{'CHAN_NAME'} :End-of-List");
				} else {
					clientsend("NOTICE $nick :allowed fields are: 'nick user host ip rname idle'");
				}
			}
		}
	} elsif ( $nick eq $botnick ) {
		# but why should i change my own nickname? who knows...
		if ( $type eq "NICK" ) {
			$botnick = $content;
		}
	}
}
# time base event

# lets do some stuff every n time, to get informations
# from the server, around 30 seconds before i show
# the stats..

if ( !$init && $initstart ) {
	$inittime = $C{'INFO_TIME'} - ( $time{'log'} - $time{'init'} );
	if ( !$inittime{$inittime} && !($inittime % 60) ) {
		$inittime{$inittime}=1;
		if ( $inittime ) {
			ircsend("NOTICE $C{'CHAN_NAME'} :GenEthic Bot $C{'VERSION'}, (init) $inittime sec left ...");
		} else {
			ircsend("NOTICE $C{'CHAN_NAME'} :GenEthic Bot $C{'VERSION'}, (init) ready ... (first report in about 5 minutes).");
			$init = 1;
		}
	}
}

if ( $time{'log'}-$time{'old1'} > $C{'INFO_TIME'}-30 ) {
	ircsend("MAP");
	ircsend("STATS C");
	undef @HUBS;
	undef @MAP;
	ircsend("LUSERS");
	ircsend("STATS u");
	ircsend("STATS l $C{'SERVER_NAME'} \*");
	$time{'old1'} = $time{'log'};
}

# lets show the stats and put them into logfile
if ( $time{'log'}-$time{'old2'} > $C{'INFO_TIME'} ) {
	$mapcount = 0;
	foreach(reverse(sort(@MAP))) {
		($mapclient,$mapserver)=split(/:/);
		$mapcount++;
		if ( $mapserver =~ /^$C{'SERVER_NAME'}$/i ) {
			$mappos = $mapcount;
		}
	}
	if ( $oldchannels < 1 ) { $oldchannels = $channels; }
	$chandiff = $channels - $oldchannels;
	if (  $chandiff =~ /^[0-9]/ ) { $chandiff = "+$chandiff"; }
	$mapper = int(($locusers/($user1+$user2))*10000)/100;
	if ( $firstloop  eq "1" ) {
		$best_top_time = time - $best_top_time;
		$best_top_time = easytime($best_top_time);
#		ircsend("NOTICE $C{'CHAN_NAME'} :$locusers($mapper\%)/$users (+$usersmore2/-$usersless2). No $mappos/$mapcount. $channels($chandiff)chans, ircu $ircduptime, 1st user \'$best_top_nick\' $best_top_time");
		ircsend("NOTICE $C{'CHAN_NAME'} :$locusers($mapper\%)/$users (+$usersmore2/-$usersless2). No $mappos/$mapcount. $channels($chandiff)chans, ircu $ircduptime");
#		undef $best_top_time;
#		undef $best_top_nick;
	}
	$oldchannels = $channels;

	#printing rping result
	undef $hubs;
	foreach(@HUBS) {
		$hubs = "$hubs $_:$rping{$_}";
	}
	$hubs =~ s/^ //;
if ( $C{'HUB_COMMON'} ) { $hubs =~ s/$C{'HUB_COMMON'}//ig; }

	if ( $firstloop  eq "1" ) {
		$hub_link = easytime($hub_link);
		ircsend("NOTICE $C{'CHAN_NAME'} :RPING-> $hubs, hub_link $hub_link ($hub_name)");
		undef $hub_link;
		undef $hub_name;
	}
	# looking if we have to display some external data.
	if ( $C{'EXTERNAL_FILE'} ne "" ) {
		undef $ex_display;
		open(EXTERNAL,$C{'EXTERNAL_FILE'});
		while(<EXTERNAL>) {
			chop;
			($ex_name,$ex_opt,$ex_data)=split(/;/);
			if ( $ex_opt eq "ANY" ) {
				$ex_display = "$ex_display $ex_name:$ex_data";
			} elsif ( $ex_opt =~ /^[0-9]+$/ ) {
				if ( $ex_data > $ex_opt ) {
					$ex_display = "$ex_display $ex_name:$ex_data";
				}
			} elsif ( $ex_opt =~ /^\-[0-9]+$/ ) {
				$ex_opt =~ s/^.//;
				if ( $ex_data < $ex_opt ) {
					$ex_display = "$ex_display $ex_name:$ex_data";
				}
			}
		} close(EXTERNAL);
		$ex_display =~ s/^ //;

		if ( $ex_display ne "" && $firstloop eq "1" ) {
			ircsend("NOTICE $C{'CHAN_NAME'} :$ex_display");
		}
	}

	# this file can then be used to do some MRTG graphs or so...
	if ( $firstloop  eq "1" ) {
		open(LOG,">$path/var/genethic.log");
		$mapper = $mapper * 100;
		print LOG <<EOF;
LOCAL_USERS:$locusers
GLOBAL_USERS:$users
MAX_LOCAL_USERS:$maxusers
CHANNEL:$channels
POSITION:$mappos
USERS_PERCENT:$mapper
USERS_MORE:$usersmore2
USERS_LESS:$usersless2
EOF
		foreach(@HUBS) {
			if (!$rping{$_}) { $rping{$_}=0; }
			print LOG "RPING_$_:$rping{$_}\n";
		}
		close(LOG);
	}

	$usersmore2 = 0;
	$usersless2 = 0;
	$time{'old1'} = $time{'log'};
	$time{'old2'} = $time{'log'};
	$firstloop = "1";

	# now the report has been done

	ircsend("WHO $C{'SERVER_NAME'} x%nuhilr");

	if ( -r "$path/tmp/who.txt" ) { system("rm $path/tmp/who.txt"); }
}
if ( $lcheck < time ) { checktosend(); $lcheck = time; }
}

# SUB to manage the outgoing messages queue
# and to slow it down if needed, to avoid
# Excess_flood disconnects..

sub clientsend {
	$msg4srv = shift;
	$msg4srv =~ s/\#\#20/\[/g;
	$msg4srv =~ s/\#\#21/\]/g;
	$msg4srv =~ s/\#\#22/\|/g;
	$msg4srv =~ s/\#\#23/\^/g;
	$msg4srv =~ s/\#\#24/\`/g;
	$msg4srv =~ s/\#\#25/\'/g;
	$msg4srv =~ s/\#\#26/\~/g;
	$msg4srv =~ s/\#\#27/\\/g;
	$msg4srv =~ s/\#\#28/\{/g;
	$msg4srv =~ s/\#\#29/\}/g;

        if($C{'DEBUG'}>1) { debug("Pushed msg \"$msg4srv\" to CLIENTqueue"); }
	if ( $C{'OPS_INFO_ONLY'} eq "TRUE" || $C{'CHAN_OPER_ONLY'} ne "TRUE" ) {
	        $msg4srv =~ s/^NOTICE $C{'CHAN_NAME'} :/NOTICE \@$C{'CHAN_NAME'} :/;
	}
	push(@TOSEND2,"$msg4srv");
}

sub ircsend {
	$msg4srv = shift;
	$msg4srv =~ s/\#\#20/\[/g;
	$msg4srv =~ s/\#\#21/\]/g;
	$msg4srv =~ s/\#\#22/\|/g;
	$msg4srv =~ s/\#\#23/\^/g;
	$msg4srv =~ s/\#\#24/\`/g;
	$msg4srv =~ s/\#\#25/\'/g;
	$msg4srv =~ s/\#\#26/\~/g;
	$msg4srv =~ s/\#\#27/\\/g;
        $msg4srv =~ s/\#\#28/\{/g;
        $msg4srv =~ s/\#\#29/\}/g;


	if($C{'DEBUG'}>1) { debug("Pushed msg \"$msg4srv\" to SERVERqueue"); }
	if ( $C{'OPS_INFO_ONLY'} eq "TRUE" || $C{'CHAN_OPER_ONLY'} ne "TRUE" ) {
		$msg4srv =~ s/^NOTICE $C{'CHAN_NAME'} :/NOTICE \@$C{'CHAN_NAME'} :/;
	}
	push(@TOSEND,"$msg4srv");
}

sub checktosend {
	$qcount  = @TOSEND;
	$qcount2 = @TOSEND2;
	
	if($C{'DEBUG'}>1) { if ( $qcount2 > 0 ) { debug("CLIENT Queue has $qcount2 msgs waiting"); } }
	if($C{'DEBUG'}>1) { if ( $qcount > 0 ) { debug("SERVER Queue has $qcount msgs waiting"); } }
	if ( ($time{'log'}-$time{'old3'}) >= $C{'TIME_FACTOR'} ) {
		if ( $qcount ) {
			$tosend=shift(@TOSEND);
			print $ircserver "$tosend\n";
			if($C{'DEBUG'}) { debug("OUT $tosend"); }
			$time{'old3'} = $time{'log'};
		} elsif ( $qcount2 ) {
                        $tosend=shift(@TOSEND2);
                        print $ircserver "$tosend\n";
                        if($C{'DEBUG'}) { debug("OUT $tosend"); }
			$time{'old3'} = $time{'log'};
		}
	}
}

# SUB to manage the URGENT outgoing messages
# Should ONLY be used for server PING replies
# had to do it, cause if the outqueue is full
# the ping reply to the server  may take a while
# and the bot disconnects ;(

sub quicksend {
	$text_to_send = shift;
	print $ircserver "$text_to_send\n";
	if($C{'DEBUG'}) { debug("OUT $text_to_send"); }
}

# SUB For loading/reloading the config file
# be carefull, an error in config may the bot STOP running

sub load_conf {

	open(CONF,"$path/etc/genethic.conf") || die "$path/etc/genethic.conf not found";
	while(<CONF>) {
		chop;
       		 if ( $_ =~  /^[A-Z]+/ ) {
			($name,$value)=split(/:/);
			$C{$name} = $value;
		}
	} close(CONF);

	undef @HELP;
	open(HELP,"$path/etc/help.txt") || die "$path/etc/help.txt not found";
	while(<HELP>) { chop; push(@HELP,$_); } close(HELP);

	foreach(split(/ /,$C{'NICKS'})) {
		push(@NICKS,$_);
	}

	if ( !$first ) { $botnick = shift(@NICKS); }

	# checking some stuff in config, hope you configured correctly ;p

	if ( $C{'CONN_DST_IP'} =~ /^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$/ ) { null; } else { print STDERR "CONN_DST_IP error\n"; $err++; }
	if ( $C{'CONN_DST_PORT'} < 1 || $C{'CONN_DST_PORT'} > 65535 ) { print STDERR "CONN_DST_PORT error\n"; $err++; }
	if ( $C{'CONN_SRC_IP'} =~ /^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$/ ) { null; } else { print STDERR "CONN_SRC_IP error\n"; $err++; }
	if ( $C{'USER_IDENT'} eq "" ) { print STDERR "USER_IDENT error\n"; $err++; }
	if ( $C{'USER_RNAME'} eq "" ) { print STDERR "USER_RNAME error\n"; $err++; }
	if ( $C{'NICKS'} eq "" ) { print STDERR "NICKS error\n"; $err++; }
	if ( $C{'X_AUTH'} != /^(TRUE|FALSE)$/ ) {  print STDERR "X_AUTH error\n"; $err++; }
	if ( $C{'O_USER'} eq "" ) { print STDERR "O_USER error\n"; $err++; }
	if ( $C{'O_PASS'} eq "" ) { print STDERR "O_PASS error\n"; $err++; }
	if ( $C{'HACK4_EXPIRE'} =~ /^[0-9]+$/ ) { $ok; } else { print STDERR "HACK4_EXPIRE error ($C{'HACK4_EXPIRE'})\n"; $err++; }

	if ( $C{'CHAN_NAME'} =~ /^\&/ ) {
		$ok;
	} elsif ( $C{'CHAN_NAME'} =~ /^\#/ && !$C{'CHAN_RUNNING_THE_BOT_ON_A_GLOBAL_CHANNEL_IS_A_SECURITY_HOLE'} ) {
		print STDERR "CHAN_NAME error\n";  $err++;
	} else {
		print STDERR "CHAN_NAME error\n";  $err++;
	}

	if ( $C{'TIME_FACTOR'} =~ /^[0-9]$/ ) { $ok; } else { print STDERR "TIME_FACTOR error\n"; $err++; }

	if ( $C{'CHAN_KEEP_MODES'} != /^(TRUE|FALSE)$/ ) {  print STDERR "CHAN_KEEP_MODES error\n";  $err++; }
	if ( $C{'CHAN_OPER_ONLY'} != /^(TRUE|FALSE)$/ ) {  print STDERR "CHAN_OPER_ONLY error\n";  $err++; }

	if (  $err ) {
		if ( !$first ) {
			print STDERR "$err errors found in configuration!\n";
			print STDERR "Please check your config file\n";
			exit 1;
		} else {
			ircsend("NOTICE $nick :!!!WARNING!!! at least $err errors found in configuration file");
			ircsend("NOTICE $nick :I will now have to quit, sorry ;-(");
			ircsend("QUIT :Configuration error");
		}
	}
}

sub easytime {
        $total = shift;
        $total = abs(time - $total);

        $sec = $total % 60;
        $total = ($total-$sec)/60;

        $min = $total % 60;
        $total = ($total-$min)/60;

        $hour = $total % 24;
        $total = ($total-$hour)/24;

        $day  = $total % 7;
        $total = ($total-$day)/7;

        $week = $total;

	foreach(split(/ /,'week day hour min sec')) { if ( !$$_ ) { $$_ = '0'; } }
        if    ($week) { $return = $week . "w" . $day  . "d" . $hour . "h" ; }
        elsif ($day ) { $return = $day  . "d" . $hour . "h" . $min  . "m" ; }
        else          { $return = $hour . "h" . $min  . "m" . $sec  . "s" ; }

        return $return;
}

sub debug {
	$dtext = shift;
	if ( $C{'DEBUG_FILE'} ) {
		open(DEBUG,">>$C{'DEBUG_FILE'}");
		print DEBUG time . " $dtext\n";
		close(DEBUG);
	}
}
