#!/bin/perl 

########################################
## Name:    GenEthic
## Version: 1.0.9
## Author:  Pascal Gloor aka sPALe
## Date:    15.10.2002
########################################

# NOTHING SHOULD BE CHANGED IN THIS FILE

use IO::Socket;
use DBI;

# 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;

# connecting to IRC Server and entering in main loop

while(<$ircserver>) {
	chop; chop;
	$line = $_;

	if($C{'DEBUG'}) {
		$noprint = 0;
		if ( $C{'DEBUG_HIDE_WHO'} ) {
			if ( $line =~ / 354 / ) { $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"); }
	}

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

# 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 eq "" ) {
		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");
	}

# in this part i manage 'time base event'
# that's why, having an Y line with 10 seconds
# pings is very important

	# lets do some stuff every n time, to get informations
	# from the server, around 30 seconds before i show
	# the stats..
	if ( $time{'log'}-$time{'old1'} > $C{'INFO_TIME'}-30 ) {
		ircsend("MAP");
		ircsend("STATS C");
		@HUBS = ''; $null = shift(@HUBS);
		@MAP = ''; $null = shift(@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 in $C{'INFO_TIME'} sec). No $mappos/$mapcount. $channels($chandiff)chans, ircu $ircduptime, top user \'$best_top_nick\' $best_top_time");
			undef $best_top_time;
			undef $best_top_nick;
		}
		$oldchannels = $channels;

		#printing rping result
		$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");
			undef $hub_link;
		}
		# looking if we have to display some external data.
		if ( $C{'EXTERNAL_FILE'} ne "" ) {
			$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, lets quickly do a WHO to fillup the DB
		ircsend("WHO $C{'SERVER_NAME'} x%nuhilr");
		mysqlgaz("DELETE FROM $C{'SQL_TABLE'}");
		mysqlstop();
	}

# 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 =~ /^:/ ) { $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 ://;

			mysqlgaz("INSERT INTO $C{'SQL_TABLE'} VALUES (\'$cnick\',\'$cuser\',\'$chost\',\'$cip\',\'$cidle\',\'$crname\')");
			mysqlstop();
			
		# end of WHO, searching for clones..
		} elsif ( $type eq "315" ) {
			$gline=1;
			foreach(@TOSEND2) { if ( $_ =~ /^GLINE /i ) { $gline=0; } }
			if($gline) {
				foreach(split(/\,/,$C{'RNAME_WHITE_LIST'})) { push(@WL,$_); }
				mysqlgaz("SELECT user,ip,rname FROM $C{'SQL_TABLE'} ORDER by rname");
				while (@row = $sth->fetchrow_array()) {
					foreach(@WL) { if ( $row[2] =~ /^$_$/ ) { undef $row[0]; undef $row[1]; } }
					if ( $lastrname ne $row[2] ) {
						if ( $rnamecounter >= $C{'RNAME_LIMIT'} ) {
								if($C{'DEBUG'}>1) { debug("DEBUG: GLINE RealName \"$lastrname\" $rnamecounter hosts found"); }
								clientsend("NOTICE $C{'CHAN_NAME'} :Auto Gline $rnamecounter hosts with Realname \"$lastrname\"");
								foreach(sort(@RHOSTS)) {
									if ( $_ ne $lastrhost ) {
										clientsend("GLINE +$_ $C{'RNAME_GLINE_TIME'} :auto gline"); 
									}	
									$lastrhost = $_;
								}
								clientsend("NOTICE $C{'CHAN_NAME'} :Auto Gline \"$lastrname\" done.");
								undef $lastrhost;
						}
						$rnamecounter = 1;
						undef @RHOSTS;
					} else {
						if ($row[0] ne '' && $row[1] ne '' ) {
							push(@RHOSTS,"$row[0]\@$row[1]");
							$rnamecounter++;
						}
					}
	
					$lastrname = $row[2];
	
				} mysqlstop();

				if ( $rnamecounter > $C{'RNAME_LIMIT'} ) {
					if($C{'DEBUG'}>1) { debug("DEBUG: GLINE RealName \"$lastrname\" $rnamecounter hosts found"); }
					clientsend("NOTICE $C{'CHAN_NAME'} :Auto Gline $rnamecounter hosts with Realname \"$lastrname\"");
					foreach(sort(@RHOSTS)) {
						if ( $_ ne $lastrhost ) {
							clientsend("GLINE +$_ $C{'RNAME_GLINE_TIME'} :auto gline");
						}
						$lastrhost = $_;
					}
					clientsend("NOTICE $C{'CHAN_NAME'} :Auto Gline \"$lastrname\" done.");
				}

				undef $lastrhost;
				undef @RHOSTS;
	
				undef $rnamecounter;
				undef $lastrname;
	
				undef @WL;
			}
			undef $gline;
			
		# getting list of users when entering in channel, whois'ing them
		} elsif (  $type eq "353" ) {
			$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_link = $content;
				$hub_link =~ s/.*://;
				$hub_link = time - $hub_link;

			} 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 ( $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'}" ) {
			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'}");
				} elsif ( $content =~ /^GENDER/ && $C{'GENDER'} ne '' ) {
	                        	ircsend("NOTICE $nick :GENDER $C{'GENDER'}");
	                	}

			# 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';

					mysqlgaz("SELECT user FROM $C{'SQL_TABLE'} WHERE user like \'$suser\' AND ( host like \'$shost\' OR ip like \'$shost\' )");
					while (@row = $sth->fetchrow_array()) { $scount++; }

					clientsend("NOTICE $nick :Scanned ($content), $scount users found.");
				} elsif ( $content =~ /^.+$/ ) {
					$ssearch = $content;
					$ssearch =~ s/\*/\%/g;
					$scount = '0';
					mysqlgaz("SELECT user FROM $C{'SQL_TABLE'} WHERE rname like \'$ssearch\'");
					while (@row = $sth->fetchrow_array()) { $scount++; }

					clientsend("NOTICE $nick :Scanned rname ($content), $scount users found.");

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

			# checking for clones
			} elsif ( $content =~ /^clones/i ) {
				$content =~ s/^clones //i;
				if ( $content =~ /^(nick|user|host|ip|rname|idle)(\ |$)/i ) {
					$table = $content;
					$table =~ tr/[A-Z]/[a-z]/;
					$table =~ s/ .*//;
					$content =~ s/$table//;
					$content =~ s/^ +//;
					$ccount = 0;
					if($content) {
						mysqlgaz("SELECT $table FROM $C{'SQL_TABLE'} WHERE $table like \'$content\' ORDER by $table");
						clientsend("NOTICE $C{'CHAN_NAME'} :clones/botnets detection on field \'$table\' like \'$content\' (requested by: $nick)");
						while (@row = $sth->fetchrow_array()) { $ccount++; } mysqlstop();
						
						clientsend("NOTICE $C{'CHAN_NAME'} :$ccount users found.");
					} else {
						mysqlgaz("SELECT $table FROM $C{'SQL_TABLE'} ORDER by $table");
						clientsend("NOTICE $C{'CHAN_NAME'} :clones/botnets detection on field \'$table\' (requested by: $nick)");

						while (@row = $sth->fetchrow_array()) {
							$cfield = $row[0];
							if($table eq 'ip') { $cfield =~ s/\.[0-9]+$/.\*/; }
							elsif ( $table eq 'host' ) {
								$ctld = $cdom = $cfield;
								$ctld =~ s/.*\.//g;
								$cdom =~ s/\.$ctld$//;
								$cdom =~ s/.*\.//g;
								$cfield = "*.$cdom.$ctld"; }
							elsif ( $table eq 'idle' ) { 
								if ( $cfield < 3600 )        { $cfield = '0       - 1 hour'; }
								elsif ( $cfield < 86400 )    { $cfield = '1 hour  - 1 day'; }
								elsif ( $cfield < 604800 )   { $cfield = '1 day   - 1 week'; }
								elsif ( $cfield < 18144000 ) { $cfield = '1 week  - 1 month'; }
								else {                         $cfield = '1 month - more'; }
								
							}
									
							push(@CUSERS,$cfield);
							$ccount{$cfield}++;
						} mysqlstop();

						foreach(sort(@CUSERS)) {
							if ( $clast ne $_ ) {
								$cnumber = "00000$ccount{$_}";
								$cnumber =~ s/.*(....)$/$1/;
								push(@CUSERS2,"$cnumber:$_");
							}
							$clast = $_;
						}
						$ccount=1;
						foreach(reverse(sort(@CUSERS2))) {
							if ( $ccount <= '5' ) {
								$cnumber = $cname = $_;
								$cnumber =~ s/:.*//;
								$cname   =~ s/$cnumber://;
								undef $noprint;
								undef $comment;
								if ( $table eq 'rname' ) {
									$comment = "(max $C{'RNAME_LIMIT'})";
									foreach(split(/\,/,$C{'RNAME_WHITE_LIST'})) {
										if ( $cname =~ /^$_$/ ) { $noprint = 1; }
									}
								}

								if(!$noprint) { clientsend("NOTICE $C{'CHAN_NAME'} :$ccount: $cnumber$comment users found with \'$cname\'"); $ccount++; }
							}
						}
						undef @CUSERS;
						undef @CUSERS2;
						clientsend("NOTICE $C{'CHAN_NAME'} :End-of-List");
					}
					undef %ccount;
				} 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;
		}
	}
	checktosend();
}

# 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=0;
	$qcount2 = 0;
	foreach(@TOSEND) { $qcount++; }
	foreach(@TOSEND2) { $qcount2++; }
	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 ( $msgcount eq '' ) { $msgcount = 0; }
	if ( $time{'log'}-$time{'old3'} > $msgcount ) {
		$msgcount = 0;
		if ( $qcount > 0 ) {
			for($loop=0; $loop<4; $loop++ ) {
				$tosend=shift(@TOSEND);
				if ( $tosend ne '' ) {
					$msgcount++;
					print $ircserver "$tosend\n";
					if($C{'DEBUG'}) { debug("OUT $tosend"); }
				}	
			}
		} else {
	                for($loop=0; $loop<3; $loop++ ) {
	                        $tosend=shift(@TOSEND2);
	                        if ( $tosend ne '' ) {
	                                $msgcount++;
	                                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;
			print "CONFIG: $name = \"$value\"\n";
		}
	} close(CONF);

	@HELP=''; $null = shift(@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 eq "" ) {
		$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{'CHAN_NAME'} != /^\&/ ) { print STDERR "CHAN_NAME 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 >= 1 ) {
		if ( $first eq "" ) {
			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 = 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;

        undef $return;
        if ($week) { $return .= $week . "w"; }
        if ($day)  { $return .= $day  . "d"; }
        if (!$week && $hour) { $return .= $hour . "h"; }
        if (!$week && !$day && $min)  { $return .= $min  . "m"; }
        if (!$week && !$day && !$hour && $sec)  { $return .= $sec  . "s"; }


        return $return;
}

sub mysqlgaz {
	$sqlquery = shift;
	if($C{'DEBUG_SQL'}) { debug("SQL_CMD: $sqlquery"); }
        $dbh   = DBI->connect("DBI:mysql:$C{'SQL_DBASE'}", $C{'SQL_USER'}, $C{'SQL_PASS'});
        $sth   = $dbh->prepare($sqlquery);
        $rv    = $sth->execute() || die $dbh->errstr();
}

sub mysqlstop {
        $dbh->disconnect();
}

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