#!/usr/bin/perl -w
#
# SDclone by dccserv for xchat (v1.8.6+).
# I'm not a perl master, don't kick my ass for lame code.
# I just start learning perl with that script... (ok i'm now better at perl, but still not a master)
# Released under GNU General Public License
#
# Needed to run: Perl 5 (vital), ls, uptime, uname, /proc & cat (should be handle by your OS) and UP (for your stats)
# If you don't run Linux, there is a compatibility var that can be set to help sdclone works with other OS
# Set $openBSD="true"; to activate it.
# You can found UP there http://www.owenmeany.com/lfiles/up-0.3.tar.gz
# UP is from David Cantrell  (http://www.burdell.org)
# I have mirror it to http://sdclone.openbsd.cx/files/up-0.3.tar.gz or !dccserv up-0.3.tar.gz
# Script got his own page now : http://sdclone.openbsd.cx/
# and you can email me there: dccserv@sdclone.etechnova.net
#
# Differences with SDFIND
# - No Top 10 function
# - No que promotion (for voice, op...)
# - No cps limit !!! I REALLY REALLY HATE THAT
#   - Some servers set cps limit to 10000cps with 2 slots, they think it's good because 2*10000cps=20000cps and they can do 30000cps
#      NO ! It's not  if 2 users with DSL (64000cps) download a file, then slot 1 is eaten by 1st user, how many cps remain for slot 2 user ?
#      We don't need that lame function, we use Dynamic Slots to optimize our bandwith
#
#
#
# thanks to :
# Androids  - Thanks for everything
# mp3linux  - Thanks for testing and comments
# hhallian  - Thanks for your efforts on translation to spanish & testing help <-- where you ???
# nomada    - Thanks for your efforts on translation to spanish...
# Slackster - For your comments, your help and for sharing your files...
#             If only everyone could be like you on internet...
# Storml0rd - Thanks for the domain, for the shell, for the mail, for testing with BSD, for your comments and for the
#             www ! Eh this make a lot of thanks... Oh i forgot, i hate you for forcing me changing the list building !!!
#
#
####################################################################

#################################################
# imporant variables to define are here         #
$SDLang="fr";                                   # this define the language SDClone should talk by default
$SDUserLang="gb";                               # default language to speak for a user, this is the default
                                                # language assign to a new user (english is of course the best
                                                # choice to start)
$mainDir="/mp3share";    		        # directory to find files to share
$followLink="true";				# unset to stop follow links
$Trigger="dccserv";				# trigger to use (your nick is also a trigger). NO SPACE ALLOWED
@chans=();					# hold the channels where we serv files
						# put in chans the channels where you want to share files
						# push @chans, "#a_channel_to_serve_files_in";
						# by default #bluehouse, my home on undernet :-)
push @chans, "#drumnbass";                      # our serving channels........
push @chans, "#bluehouse";
push @chans, "#mp3_goth";
push @chans, "#technomp3";
$slotMin=1;                                     # min slots - don't put a too high value ;)
$slotMax=4;                                     # max slots - also don't put a too high value and should be "min < max"
                                                # to disable dynamic slots just set "min = max"
$queNick=8;                                     # maximum file in que by person
$allowFind="true";                              # true allow @find, false disable
$maxFind=6;                                     # Display maxFind files results
$findCmd='@find';                               # You can even change the @find trigger
$useMOTD="true";                                # enable/disable "Message Of The Day" in your list, it will appears just
                                                # between the files and SDClone header
$unVoice="true";                                # allow script to inform serving channel to devoice us
$ServerEnable="false";                          # allow script to be control by SDServer script
$ServerKey="adefaultpassword";                   # it's the password we use : must be the same of course as in SDServer
                                                # No SPACES in the password !
$autoJoin="true";                               # when script goes ON, auto join our serving channels
$autoBan="true";                                # auto-ban a user that do sucking stuff :
                                                # like @find *.mpg or @find *.* or hammer with -que
$autoBan_que="true";                            # ban for short -que request
$autoBan_all="true";                            # ban for @find *.*
$autoBan_DNS="true";                            # ban if two users with the same dns are in que (cheating)
$autoBan_ext="false";                           # ban for @find .*
$noUnderscore="true";                           # convert _ to space in filenames in your list (not on disk !)
$showGet="true";                                # use in !trig-slots -> false to not display your current gets
$showSend="true";                               # use in !trig-slots -> false to not display your current sends
$SDHandleSend="auto";                           # default to auto, can be change with /sdcontrolsend
$userTracking="true";                           # if "true", stats of users are saved + user can choose a language
                                                # if "false", no stats + user use the same language as sdclone
                                                # This is a cool feature, only put it to false to saved hd space
$userTrackingOnFind="true";                     # false, only track users that ask something with our nick (save space)
                                                # true, same as false + track users that use @find command too
                                                # If $userTracking is false, $userTrackingOnFind is also false
                                                # if $allowFind is false, $userTrackingOnFind is also false
                                                # when set to false, script will answer to find request in the
                                                # same language as the script talk, not in the user prefered
                                                # language...
$ShowOnComplete="true";                         # Show to a user his statistics when a file send is complete
                                                # disable if $userTracking is false
$ShowOnCompletePrivate="false";                 # Stats are show private to the user, if set to false, show
                                                # to serving channels, could be annoing, but some ppl like that
$openBSD="false";                               # For users with openBSD and other non Linux release, in that
                                                # mode sdclone use uname rather than /proc to get infos
$useSeen="true";                                # if true, we will anwser to a !seen request if we have info on that nick
#################################################
# This affect the way the scripts works, not vital but useful
$MOTDfile="sdclone-motd.txt";                   # MOTD file, load from where we also put our list
$SDcolor='%C15%O';				# color - on 1.1+ overwrite by skin
$SDcolorh='%C3%B';				# highlighted color - on 1.1+ overwrite by skin
$useButtons="true";                             # false to disable buttons in xchat
$useZIP="true";		        		# true zip your list, false leave it as a text file
$unVoiceMsg="!devoice";                         # this is what we will output on devoice
$checkTimer=30000;				# timer for checking to send file... (1000 = 1s)
$bannerTimer=350000;				# timer for banner
$DynamicSlots=2;				# Dyncamic Slots feature, choose a method, save in cfg
							# 0 - Record Send / 2
							# 1 - Record Send - $DynamicValue
							# 2 - $Dynamic Value
$DynamicValue=12500;				# Should open a new slot if we can handle another one 56k user
$shoutRecord="true";                            # true show when record is broken in serving channel, false disable
$smallFile="true";                              # true to use small file rules, false disable
$smallSize=512000;                              # size in bytes that define a small file ie: 512kb
$otherFile=3;                                   # Number of files we are allow to send : $slotMax + $otherFile... include smallfiles + list
# Small files rules are : if file size is < $smallSize then send it NOW if total slots in use is < $otherFile + $slotMax
# So if the file is define as "small file" then the user requesting that file will wait less less less time in que to get it.
$dailyReport="true";                            # true output some stats to channels :
                                                # number of files sends, number of kb sends, daily record cps & nick, uptime
$useLog="true";                                 # Enable logging of what SDClone does...
$listShowSize="true";                           # Build your list with filesize show or hidden
$sdShowUpload="true";                           # Open and track uploaded files
$sdShowDownload="true";                         # Open and track downloaded files
###############################################
# Here you should not change something if you don't what you are doing
$SDWorking=4;                                   # How SDClone is running
                                                    # 0 - Disable
					            # 1 - No banner, sends on, no find, no adding files to que, no sending list
                                                    # 2 - No banner, no sends, find (if enable), adding files to que, sending list
                                                    # 3 is reserved for later use if need
                                                    # 4 - Fully working
$listChars=100;                                     # Define how many "----" we put in our list
                                                # use for "!trigger afilename.test ---------- 12m", see your list for a real example
$cfgFile="sdclone.cfg";				# filename to save our config
$lstFile="sdque.cfg";				# filename to save our que
$sdLogFN="sdclone.log";                         # filename for the log file
$sdbanFN="sdclone.ban";                         # filename for the ban list
$sdtab="]sdclone[";
$Ver="2.1-emacs";                               # version number
$Author="dccserv";                              # hey it's just me :-)
$slotFree=$slotMax;				# slots free
$slotUse=0;                                     # slots in use
$CPScurrent=0;					# current bandwith in use ($CPSget+$CPSsend)
$CPSget=0;					# bandwtih in use for getting file
$CPSsend=0;					# bandwith in use for sending file
$CPSgetfile=0;					# number of files we are getting
$CPSsendfile=0;					# number of files we are sending
$CPSwaitfile=0;                                 # number of files waiting to be upload
$CPSrecordDay=0;                                # top send this day, not save in cfg
$CPSrecordNickDay="none";                       # top send nickname this  day, not save in cfg
$CPSrecord=0;					# top send, save in cfg
$CPSrecordNick="none";				# top send nickname, save in cfg
$listDate="none";				# list date
$listDateM=0;                                   # list date month
$listDateD=0;                                   # list date day
$listDateY=0;                                   # list date year
$firstDate="none";				# first time use, save in cfg
$listSend=0;					# List send counter, save in cfg
$totalFiles=0;					# total files you share
$totalKB=0;					# total kb you share
$bufferTimer=2;                                 # timer for our buffer = 2 seconds DON'T TOUCH if you don't know what it does
$bufferTimerSlice=1;                            # just to count bufferTimer, must be 1 DO NOT CHANGE !
$dailyTimer=0;                                  # use with $bufferTimer, to count a day
$statsSave=0;                                   # use to autosave stats...
$totalSendKB=0;					# Stats: already send, amount KB, save in cfg
$totalSendFile=0;				# Stats: already send, total files, save in cfg
$lowestWait="";                                 # Formatted String of $lowestTime
$buildingList=0;				# set to 1 if we are building list
$newList="";
$zipList="";					# to keep the zip file name
$oldList="";					# save in cfg to clean the list when building a new one
$queMax=999;					# for info, in fact not use except for ctcp
$check_nick_free_que=0;				# use to check if nick can add files to que
$check_nick_file_que=0;				# use to check if nick already have that file in que
%file2send=();					# Store informations about a file, fill by routines that answer through that table
$lgVersion="";                                  # Store infos on Version for language
$lgAuthor="";                                   # Store infos on Author for language
$lgType="";                                     # Store infos on Language type for language
$skinVersion="";                                # Store infos on Version for Skin
$skinAuthor="";                                 # Store infos on Author for Skin
$skinType="";                                   # Store infos on Language type for Skin
$SDNowLang="gb";                                # Currently use language
push @file2send, "none";			# 0 - File name
push @file2send, "none";			# 1 - File path
push @file2send, 0;				# 2 - File Size (for stats)
push @file2send, "none";			# 3 - nick that want/search file
push @file2send, "none";			# 4 - channel where file is request
push @file2send, "none";			# 5 - server where file is request
$myUserHost="hostname";         # store infos about a user
$allGood="true";                # do not change !
%queFile=();			# hold que parameters (to save it) - File name
%quePath=();			# hold... - file path name
%queFSize=();			# hold ... - file size
%queUser=();			# hold... - Nickname in que
%queChan=();			# hold... - Channel
%queWait=();			# hold... - Waiting time
%queServer=();			# hold... - Server where is nick
%queIdent=();                   # hold... - Ident of user
$filesActive=0;                 # Active files in que
                                # Que Size - Active Files = # Files hold in que waiting a user comeback
$usersActive=0;                 # Active users in que (that are present)
%internalList=();		# hold values pointing to files
%externalList=();		# hold values for the list itself (the one send to other)
%dcc_nick=();			# sends values
%dcc_file=();
%dcc_cps=();
%dcc_size=();
@sd_buffer=();                  # our buffer for output
$customAds=0;                   # secs before display our custom ads
$customAdsEnable="false";       # set to true to display the ads, use /sdcustom
@MOTD=();                               # for loading MOTD
@LangTable=();                          # hold traduction for languages
@LangInfo=();                           # hold info on all languages. authors...
@LangIndex=();                          # an index of load languages, see sd_load_lang for more
$nbLang=0;                              # track number of languages we found/use
@SkinInfo=();                           # hold info on all skins, authors...
@SkinTable=();                          # hold traduction for skins
@banList=();                            # ban list
@queReqCount=();                        # for que request
@queReqUser=();                         # for que request
@SkinMaker=();                          # Got the latest build skin
$ServerNick="";                         # this is use to get info on our server
$ServerCmd="";                          # Server command gives to us
$ServerPwd="";                          # Pwd server gives us
$ServerData="";                         # Additional datas server sends us
$ServerLast="none";                     # Last nick of server we know
$ServerEcho="false";                    # don't touch, use to output to server our messages !
$BackServerEcho="false";                # use to backup, for list because of delayed messages...
$SDSpacer="==";                         # bah it's the spacer, overwrite by loading a skin
$dispLang=0;                            # use to track lines for a language
$dispSkin=0;                            # same use as $dispLang but for skins
$forceSendCancel="false";               # When true, stop all current sends, then unset it to false
$forceSend=-1;                          # -1 = nothing, 0..x force sending of file x in que
$sdDebug=0;                             # 1 for debugging messages (you won't see any on final version)
$sdDumpBuffer=0;                        # 1 to show the buffer use in sdclone
$mlist1="";                             # use to build list (external call)
$mlist2="";                             # use to build list (external call)
$tf_fastSeverity=0;                     # Severity of "target change too fast" error. All tf_ are realate to that error
$tf_BuffTimer=0;                        # to save original buffer timer value
$tf_findNumber=0;                       # to save original find answer
$tf_findStatus="true";                  # to save original find status
IRC::register("SDClone",$Ver,"sd_shutdown","");
IRC::command("/QUERY $sdtab");
sd_talk("SDClone v$Ver by $Author init... ");
IRC::add_command_handler("sdList","sd_build_list");
IRC::add_command_handler("sdQue","sd_view_que");
IRC::add_command_handler("sdkillque","sd_que_killer");
IRC::add_command_handler("sdLoad","sd_load_cfg");
IRC::add_command_handler("sdSet","sd_set_function");
IRC::add_command_handler("sdHelp","sd_show_help");
IRC::add_command_handler("sdfriend","sd_friend_que");
IRC::add_command_handler("sdSave","sd_save_cfg");
IRC::add_command_handler("sdstatus","sd_get_status");
IRC::add_message_handler("439","sd_handle_fast");
IRC::add_message_handler("JOIN", "sd_user_join");
IRC::add_message_handler("PART", "sd_user_part");
IRC::add_message_handler("QUIT", "sd_user_quit");
IRC::add_message_handler("NICK", "sd_nick_change");
IRC::add_message_handler("PRIVMSG","sd_analyse");
IRC::add_message_handler("NOTICE","sd_master");
IRC::add_command_handler("sdPub","sd_publicity");
IRC::add_command_handler("sdclone","sd_run");
IRC::add_command_handler("sdban","sd_ban");
IRC::add_command_handler("sdunban","sd_unban");
IRC::add_command_handler("sdbanlist","sd_banlist");
IRC::add_command_handler("sdsay","sd_Say");
IRC::add_command_handler("sdsaychannel","sd_SayChannel");
IRC::add_command_handler("sdjoin","sd_join");
IRC::add_command_handler("sdleave","sd_Leave");
IRC::add_command_handler("sdclient","sd_wrong");
IRC::add_command_handler("sdconnect","sd_wrong");
IRC::add_command_handler("sdgetslots","sd_wrong");
IRC::add_command_handler("sdgetstats","sd_wrong");
IRC::add_command_handler("sdgetinfo","sd_wrong");
IRC::add_command_handler("sdgetircserver","sd_wrong");
IRC::add_command_handler("sdstartserve","sd_add_serving_channel");
IRC::add_command_handler("sdstopserve","sd_del_serving_channel");
# add in v2.0
IRC::add_command_handler("sdcontrolsend","sd_controlSend");
IRC::add_command_handler("sdsend","sd_sendNow");
IRC::add_command_handler("sdstopsend","sd_StopSend");
IRC::add_command_handler("sduserinfo","sd_UserInfo");
IRC::add_command_handler("sdclearusers","sd_clear_users");
IRC::add_command_handler("sduptime","sd_show_uptime");
IRC::add_command_handler("sdcustomads","sd_custom_ads");

IRC::add_command_handler("sdswitchlanguage","sd_switch_language"); # only in sdclone
IRC::add_command_handler("sdshowword","sd_show_word");       # only in sdclone, to helping translators & skins maker
IRC::add_command_handler("sdshowskin","sd_show_skin");       # only in sdclone, to helping translators & skins maker
IRC::add_command_handler("sddump","sd_lang_dump");           # only in sdclone, to help myself
IRC::add_timeout_handler($checkTimer,"sd_loop_timer");	     # check to send files
IRC::add_timeout_handler($bannerTimer,"sd_banner");	# check our banner
IRC::add_timeout_handler(1000,"sd_check_buffer");            # check our buffer
IRC::add_command_handler("sdtest","sd_test");                # only in sdclone, just for test

($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900;
$mon++;
# finding homepath taken from xdcc 1.3.3.pl
open (FIND, "ls -d ~ |");
@test = <FIND>;
close (FIND);
chop($test[0]);
$myhomepath = $test[0]."/.xchat/sdclone/";       # v1.1 sdclone locate now in ~/.xchat/sdclone
chomp($myhomepath);
$mlist1=$myhomepath."listmake";
$mlist2=$myhomepath."listready";
$tf_BuffTimer=$bufferTimer;              # to save original buffer timer value
$tf_findNumber=$maxFind;                 # to save original find answer
$tf_findStatus=$allowFind;               # to save original find status

&sd_init_load_lang;
if ($allGood eq "true") { &sd_load_skin; &sd_change_static; }
if ($allGood eq "true") {
                        $CPSrecordNickDay=&sd_get_lang(0);
                        $CPSrecordNick=&sd_get_lang(0);
                        }
if ($allGood eq "true") { &sd_load_cfg;  }
if ($allGood eq "true") { &sd_load_que; }
if ($allGood eq "true") { &sd_load_ban; }
if ($allGood eq "true")
        {
        &sd_get_lang_Index($SDLang);
        &sd_change_static;
        &sd_talk("SDClone: Ready.");
        if ($useButtons eq "true")
                {
                IRC::command("/addbutton SD-ON /sdclone on");
                IRC::command("/addbutton SD-OFF /sdclone off");
                IRC::command("/addbutton SD-Help /sdhelp");
                IRC::command("/addbutton SD-Status /sdstatus");
                }
        if ($useLog eq "true")  { &sd_talk("Starting log session on ".sd_format_date("$mday $mon $year ".@LangTable[79+$dispLang])); }
        }
else    {
        &sd_talk("SDClone: NOT READY !!! Something is bad... DO NOT USE THE SCRIPT in that state !");
        $SDWorking=0;
        }

sub sd_controlSend
# Use to alter way the "Send control" works
{
my $line=lc(shift(@_));
if ($line eq "on")
        {
        $SDHandleSend="on";
        }
if ($line eq "off")
        {
        $SDHandleSend="off";
        }
if ($line eq "auto")
        {
        $SDHandleSend="auto";
        }
sd_talk(&sd_get_lang(424)." : ".$SDHandleSend);
return 1;
}

sub sd_debug
# output debugging messages if enable
{
my $line=shift(@_);
if ($sdDebug == 1)
        {
        IRC::command("/QUERY ]SDebug[");
        IRC::print_with_channel("$line", "]SDebug[", "");
        }
}

sub sd_out_upload
# output uploaded files to a window
{
my $line=shift(@_);
if ($sdShowUpload eq "true")
        {
        IRC::command("/QUERY ]SD-Upload[");
        IRC::print_with_channel("$line", "]SD-Upload[", "");
        }
}

sub sd_out_download
# output downloaded files to a window
{
my $line=shift(@_);
if ($sdShowDownload eq "true")
        {
        IRC::command("/QUERY ]SD-Download[");
        IRC::print_with_channel("$line", "]SD-Download[", "");
        }
}

sub sd_nick_change
# Warn user on nick change
{
my $line=shift(@_);
my @temp=split "!",$line;
my @temp2=split ":",@temp[0];
my $oldNick=@temp2[1];
@temp=split "NICK",$line;
@temp2=split ":",@temp[1];
my $newNick=@temp2[1];
# ok now let's check if nick as something on our que
sd_check_add_que("$oldNick;;;;nothing"); # check to add nothing in que, will return number
                                         # of files in que
if ($check_nick_free_que > 0)
        {
        &sd_notice($newNick." ".&sd_get_lang_user("446 $oldNick")."$oldNick".&sd_get_lang_user("447 $oldNick"));
        &sd_talk(&sd_get_lang(448)." $oldNick ".&sd_get_lang(449)." $newNick");
        }
my $isDownloading = sd_nick_getting_file($oldNick);
if ($isDownloading eq "true")
        {
        &sd_notice($newNick." ".&sd_get_lang_user("446 $oldNick")."$oldNick".&sd_get_lang_user("450 $oldNick"));
        &sd_talk(&sd_get_lang(451)." $oldNick ".&sd_get_lang(449)." $newNick");
        }
return 0;
}

sub sd_format_date
# return a date in the current date format
# on enter: "day month year" + dateformat
# on exit: date is convert to "dateformat"
# in dateformat you can use :
# da - return day 1 number if day < 10
# dd - return day 2 numbers if day < 10
# mo - return month 1 number if month < 10
# mm - return month 2 numbers if month < 10
# ms - return month as string (1..12 = january..december -> dependant on language too)
# yy - return year 2 numbers
# yyyy - return year in 4 numbers
# samples:
# sd_format_date("4 1 2002 ms, dd"); -> january, 04
# sd_format_date("4 1 2002 da-mo-yy"); -> 4-1-02
# sd_format_date("4 1 2002 mm*dd*yyyy"); -> 01*04*2002
# sd_format_date("4 1 2002 YY-ms-YY"); -> YY-january-YY
# of course don't use da, dd, mo, mm, ms, yy, yyyy except to get date
# sd_format_date("4 1 2002 yymsyy"); -> 02january02 :(
# sd_format_date("4 1 ms, dd"); -> I dunno, may crash, must pass a valid date !
{
my $line=shift(@_);
chomp($line);   # prevent \n in format
my ($d, $m, $yyyy, $format)=split " ",$line,4;
my $dd=$d;
my $mm=$m;
my $ms=@LangTable[355+$m+$dispLang];
chomp($ms);
my $yy=substr($yyyy,2,2);
if ($d < 10)   { $dd="0".+$d; }
if ($m < 10)   { $mm="0".+$m; }
$format=$format.+"**"; # promote $format with ** to avoid a fail on cut at end
$format=join($dd,split("dd",$format));
$format=join($d,split("da",$format));
$format=join($mm,split("mm",$format));
$format=join($ms,split("ms",$format));
$format=join($m,split("mo",$format));
$format=join($yyyy,split("yyyy",$format));
$format=join($yy,split("yy",$format));
$ret=substr($format,0,length($format)-2);
return $ret; # $format - **
}

sub sd_test
{
return 1;
}

sub sd_sendNow
# Force a file waiting in que to be send.
# Bypass slots limits, but must be valid (user present in a serving channel, file exist...)
{
$forceSend=shift(@_);
&sd_talk(&sd_get_lang(452)." ".@queUser[$forceSend]);
return 1;
}

sub sd_switch_language
# Use to switch to another language
# on enter the country code to switch to
{
$SDLang=shift(@_);
# &sd_load_lang; no more need
# &sd_load_skin; no more need
# &sd_change_static; no more need
return 1;
}

sub sd_handle_fast
# try to handle target change too fast error message...
# what we do to suppress it
# 1 - pushing our buffer to raise next talk to x seconds (grab from error message) (always do that on error)
# 2 - raise buffer to 4s
# 3 - lowering @find command to 3 answers
# 4 - lowering @find command to 1 answer
# 5 - disabling @find command
# 6 - increase buffer by 2s anytime we get the error
# Every 30 minutes, we try lowering to see if we can get back 1 level
#$tf_fastSeverity=0;                     # Severity of "target change too fast" error. All tf_ are realate to that error
#$tf_BuffTimer=0;                        # to save original buffer timer value
#$tf_findNumber=0;                       # to save original find answer
#$tf_findStatus="true";                  # to save original find status
#
#$allowFind="true";                              # true allow @find, false disable
#$maxFind=6;                                     # Display maxFind files results
#$bufferTimer=2;
{
$tf_fastSeverity++;     # increase level
my $errorMsg=shift(@_);
my @delayMsg=split "wait ",$errorMsg;
my @delayMsg2=split " seconds", $delayMsg[1];
&sd_talk("ERROR: Target change too fast, level $tf_fastSeverity");
&sd_talk("       - Waiting $delayMsg2[0] seconds...");
for (my $ii=0; $ii < ($delayMsg2[0] / $bufferTimer); $ii++)
        { unshift @sd_buffer, "/echo waiting $ii seconds"; }    # Waiting time for our buffer
if ($tf_fastSeverity == 2)
        {
        if ($bufferTimer < 4)   { $bufferTimer=4; }
                else            { $tf_fastSeverity++; } # already set to 4 increase level
        &sd_talk("       - Raising buffer delay to 4 seconds...");
        }
if ($tf_fastSeverity == 3)
        {
        if (($maxFind > 3) && ($allowFind eq "true")) { $maxFind=3; }
                else { $tf_fastSeverity++; }    # increase level
        &sd_talk("       - Lowering find results to 3 answers...");
        }
if ($tf_fastSeverity == 4)
        {
        if (($maxFind > 1) && ($allowFind eq "true")) { $maxFind=1; }
                else { $tf_fastSeverity++; }    # increase level
        &sd_talk("       - Lowering find results to 1 answer...");
        }
if ($tf_fastSeverity == 5)
        {
        if ($allowFind eq "true")       { $allowFind="false"; }
                else                    { $tf_fastSeverity++; }    # increase level
        &sd_talk("       - Disabling \@find command...");
        }
if ($tf_fastSeverity > 5)
        {
        $bufferTimer=($tf_fastSeverity-5)+4;
        &sd_talk("       - Raising buffer delay to $bufferTimer seconds...");
        }
if ($tf_fastSeverity > 10)
        {
        &sd_talk("       - Too much delay... You should leave some channels !");
        }
}

sub sd_dump_buffer
# output debugging messages if enable
{
my $line=shift(@_);
if ($sdDumpBuffer == 1)
        {
        IRC::command("/QUERY ]SD-Buffer[");
        IRC::print_with_channel("$line", "]SD-Buffer[", "");
        }
}

sub sd_User_Getleech
# Same as sd_UserInfo but target for user response only
# on enter: nick
{
my $nick=lc(shift(@_));
&sd_get_lang_Index(&sd_get_user_lang_pref($nick));
my $temp=&sd_format_date(sd_get_user_info("$nick 1")." ".@LangTable[79+$dispLang]);
&sd_message("$nick ".&sd_get_lang_user("439 $nick")." : $temp");
$temp=&sd_get_user_info("$nick 2");
&sd_message("$nick ".&sd_get_lang_user("440 $nick")." : $temp");
$temp=&sd_get_user_info("$nick 3");
&sd_message("$nick ".&sd_get_lang_user("441 $nick")." : $temp");
$temp=&sd_get_user_info("$nick 4");
&sd_message("$nick ".&sd_get_lang_user("442 $nick")." : $temp");
$temp=&sd_get_user_info("$nick 5");
my $upsize = sd_byte_converter($temp);
&sd_message("$nick ".&sd_get_lang_user("443 $nick")." : $temp ".&sd_get_lang_user("444 $nick")." ($upsize)");
my $cps=&sd_get_user_info("$nick 9");
&sd_message("$nick ".&sd_get_lang_user("453 $nick")." : $cps".&sd_get_lang_user("1 $nick"));
}

sub sd_UserInfo
# Get informations about a user
{
my $nick=lc(shift(@_));
my $temp=&sd_get_user_info("$nick 6");
my $temp2="unknown";
my $temp3="";
if ($temp ne "false")   { $temp2=$temp; }
&sd_talk(&sd_get_lang(445)." $nick ($temp2)");
$temp3=sd_user_isban($nick);
$temp2=&sd_get_lang(454);
if ($temp eq "false")   { $temp2=&sd_get_lang(455); }
if ($temp3 eq "true") { $temp2=&sd_get_lang(456); }
&sd_talk(&sd_get_lang(457)." : $temp2");
if ($temp ne "false")    # must be true to get that info
        {
        $temp=&sd_get_user_info("$nick 0");
        for (my $iii=0; $iii<@LangIndex; $iii++) {
                if (@LangIndex[$iii] eq $temp)        { $temp2=@LangInfo[$iii*4+1]; } }
        sd_talk(&sd_get_lang(458)." : ".$temp2);
        $temp=&sd_get_user_info("$nick 1");
        $temp2=&sd_format_date($temp." ".@LangTable[79+$dispLang]);
        sd_talk(&sd_get_lang(459)." ".$temp2);
        $temp=&sd_get_user_info("$nick 2");
        sd_talk(&sd_get_lang(460)." : ".$temp);
        $temp=&sd_get_user_info("$nick 3");
        sd_talk(&sd_get_lang(461)." : ".$temp);
        $temp=&sd_get_user_info("$nick 5"); # size of all files download
        $temp2=&sd_byte_converter($temp);
        $temp3=$temp." ".&sd_get_lang(466)." (".+$temp2.+")"; # temp3 = xxxx bytes (xxx MB)
        $temp=&sd_get_user_info("$nick 4"); # number of files
        sd_talk(&sd_get_lang(462)." : ".$temp." - ".$temp3);
        $temp=&sd_get_user_info("$nick 9"); # top cps
        sd_talk(&sd_get_lang(453)." : ".$temp.&sd_get_lang(1));
        $temp=&sd_get_user_info("$nick 7");
        $temp2=&sd_comp_time($temp);
        $temp=&sd_get_user_info("$nick 8");
        $temp2.="($temp)";
        sd_talk(&sd_get_lang(463)." : $temp2");
        }
sd_check_add_que("$nick;;;;nothing"); # check to add nothing in que, will return number
                                      # of files in que
&sd_talk(&sd_get_lang(464)." : $check_nick_free_que");
$temp=&sd_user_visible("$nick");
$temp2=lc(&sd_get_lang(5));
if ($temp eq "true")    { $temp2=&sd_get_lang(4); }
&sd_talk(&sd_get_lang(465)." : $temp2");
return 1;
}

# the byte_converter function comes from xdcc1.3.3.pl
# Modify to make it compatible with xdcc1.3.3 script witch is a good utility script. I just dislike the way it share files
# Modify again to correct bug of region dependant divisor (. & ,)
sub sd_byte_converter
{
my $line = shift(@_);
my $sdtotmegsize2 = $line;
my $sdbegin="";
my $sdend="";
my $test = 100 / 3;           # English = 33.333333... -> support for . div countries
                              # French = 33,333333...  -> support for , div countries
                              # Other countries, add your support yourself
my $isPoint = "false";
if (index($test,".") >0)	{ $isPoint = "true"; }
if ($sdtotmegsize2 > 1000000000000000000000) {
$sdtotmegsize2 = $sdtotmegsize2 / 1000000000000000000000;
if ( $isPoint eq "true")
	{
	$sdtotmegsize2 =~ /(.*)\.(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
else	{
	$sdtotmegsize2 =~ /(.*)\,(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
$sdtotmegsize2 = "$sdbegin.$sdend"." Yottabytes";
} elsif ($sdtotmegsize2 > 1000000000000000000000) {
$sdtotmegsize2 = $sdtotmegsize2 / 1000000000000000000000;
if ( $isPoint eq "true")
	{
	$sdtotmegsize2 =~ /(.*)\.(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
else	{
	$sdtotmegsize2 =~ /(.*)\,(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
$sdtotmegsize2 = "$sdbegin.$sdend"." Zettabytes";
} elsif ($sdtotmegsize2 > 1000000000000000000) {
$sdtotmegsize2 = $sdtotmegsize2 / 1000000000000000000;
if ( $isPoint eq "true")
	{
	$sdtotmegsize2 =~ /(.*)\.(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
else	{
	$sdtotmegsize2 =~ /(.*)\,(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
$sdtotmegsize2 = "$sdbegin.$sdend"." Exabytes";
} elsif ($sdtotmegsize2 > 1000000000000000) {
$sdtotmegsize2 = $sdtotmegsize2 / 1000000000000000;
if ( $isPoint eq "true")
	{
	$sdtotmegsize2 =~ /(.*)\.(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
else	{
	$sdtotmegsize2 =~ /(.*)\,(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
$sdtotmegsize2 = "$sdbegin.$sdend"." Petabytes";
} elsif ($sdtotmegsize2 > 1000000000000) {
$sdtotmegsize2 = $sdtotmegsize2 / 1000000000000;
if ( $isPoint eq "true")
	{
	$sdtotmegsize2 =~ /(.*)\.(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
else	{
	$sdtotmegsize2 =~ /(.*)\,(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
$sdtotmegsize2 = "$sdbegin.$sdend"."T";
} elsif ($sdtotmegsize2 > 1000000000) {
$sdtotmegsize2 = $sdtotmegsize2 / 1000000000;
if ( $isPoint eq "true")
	{
	$sdtotmegsize2 =~ /(.*)\.(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
else	{
	$sdtotmegsize2 =~ /(.*)\,(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
$sdtotmegsize2 = "$sdbegin.$sdend"."G";
} elsif ($sdtotmegsize2 > 1000000) {
$sdtotmegsize2 = $sdtotmegsize2 / 1000000;
if ( $isPoint eq "true")
	{
	$sdtotmegsize2 =~ /(.*)\.(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
else	{
	$sdtotmegsize2 =~ /(.*)\,(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
$sdtotmegsize2 = "$sdbegin.$sdend"."M";
} elsif ($sdtotmegsize2 > 1000) {
$sdtotmegsize2 = $sdtotmegsize2 / 1000;
if ( $isPoint eq "true")
	{
	$sdtotmegsize2 =~ /(.*)\.(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
else	{
	$sdtotmegsize2 =~ /(.*)\,(.*)/;
	$sdbegin = $1;
	$sdend = (substr ($2, 0, 1));
	}
$sdtotmegsize2 = "$sdbegin.$sdend"."k";
} else {
$sdtotmegsize2 = $sdtotmegsize2."b";
}
return $sdtotmegsize2;
}

sub sd_run
# set the new rules for running
{
my $line=lc(shift(@_));
if ($line eq "")
        {
        my $sst = &sd_get_lang(12);
        if ($SDWorking == 1)    { $sst = &sd_get_lang(13); }
        if ($SDWorking == 2)    { $sst = &sd_get_lang(14); }
        if ($SDWorking == 4)    { $sst = &sd_get_lang(15); }
        sd_talk(&sd_get_lang(16).$sst);
        }
if ($line eq "on")
        {
        $SDWorking=4;
        sd_talk(&sd_get_lang(17));
        if ($autoJoin eq "true")        { &sd_join_serving_channel; }
        }
if ($line eq "off")
        {
        if ($unVoice eq "true")  { sd_to_serving_channel($unVoiceMsg); }
        $SDWorking=0;
        sd_talk(&sd_get_lang(18));
        if ($SDHandleSend eq "on")      { sd_talk(&sd_get_lang(425)); }
        }
if ($line eq "end")
        {
        if ($unVoice eq "true")  { sd_to_serving_channel($unVoiceMsg); }
        $SDWorking=1;
        sd_talk(&sd_get_lang(19));
        }
if ($line eq "que")
        {
        if ($unVoice eq "true")  { sd_to_serving_channel($unVoiceMsg); }
        $SDWorking=2;
        sd_talk(&sd_get_lang(20));
        }
return 1;
}

sub sd_set_function
# use to setup some variables after the run
{
my $line = shift(@_);
my @actions=split " ",$line;
sd_talk(@actions[0]);
if (@actions[0] eq "dynamic")
	{
	$DynamicSlots=@actions[1];
	$DynamicValue=@actions[2];
	sd_talk(&sd_get_lang(21)." $DynamicSlots ".&sd_get_lang(22));
	}
if (@actions[0] eq "slots")
	{
	$slotMin=@actions[1];
	$slotMax=@actions[2];
	sd_talk(&sd_get_lang(23)." $slotMin");
	sd_talk(&sd_get_lang(24)." $slotMax");
	}
if (@actions[0] eq "que")
	{
	$queNick=@actions[1];
	sd_talk(&sd_get_lang(25)." $queNick");
	}
return 1;
}

sub sd_show_help
# output some help to you
{
my $line=lc(shift(@_));
if ($line eq "")
	{
        for (my $i=26; $i < 62; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdswitchlanguage")
        {
        for (my $i=81; $i < 85; $i++)     { &sd_talk(&sd_get_lang($i)); }
        }
if ($line eq "sdstatus")
	{
	sd_talk("/sdstatus");
        for (my $i=85; $i < 88; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdlist")
	{
	sd_talk("/sdlist");
        for (my $i=88; $i < 95; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdfriend")
	{
        for (my $i=95; $i < 126; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdban")
        {
        for (my $i=370; $i < 372; $i++)     { &sd_talk(&sd_get_lang($i)); }
        }
if ($line eq "sdunban")
        {
        for (my $i=372; $i < 374; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdbanlist")
        {
        sd_talk("/sdbanlist");
        for (my $i=374; $i < 375; $i++)     { &sd_talk(&sd_get_lang($i)); }
        }
if ($line eq "sdclone")
        {
        for (my $i=375; $i < 383; $i++)     { &sd_talk(&sd_get_lang($i)); }
        }
if ($line eq "sdque")
	{
        for (my $i=383; $i < 385; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdjoin")
	{
        for (my $i=124; $i < 126; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdleave")
	{
        for (my $i=126; $i < 128; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdsay")
	{
        for (my $i=128; $i < 132; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdsaychannel")
	{
        for (my $i=132; $i < 135; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdconnect")
	{
        sd_talk("/sdconnect");
        for (my $i=135; $i < 136; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdclient")
	{
        &sd_talk(&sd_get_lang(138));
        &sd_talk(&sd_get_lang(135));
        for (my $i=139; $i < 141; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdkillque")
	{
        for (my $i=141; $i < 146; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdircserver")
	{
	&sd_talk("/sdircserver");
        &sd_talk(&sd_get_lang(135));
        &sd_talk(&sd_get_lang(146));
	}
if ($line eq "sdpub")
	{
	&sd_talk("/sdpub");
        &sd_talk(&sd_get_lang(147));
	}
if ($line eq "sdgetslots")
	{
	&sd_talk("/sdgetslots");
        &sd_talk(&sd_get_lang(135));
        for (my $i=148; $i < 150; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdgetstats")
	{
	&sd_talk("/sdgetstats");
        &sd_talk(&sd_get_lang(135));
        &sd_talk(&sd_get_lang(150));
        &sd_talk(&sd_get_lang(148));
	}
if ($line eq "sdstartserve")
	{
        for (my $i=151; $i < 155; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdstopserve")
	{
        for (my $i=155; $i < 158; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdset")
	{
	&sd_talk("/sdset dynamic #1 #2");
        for (my $i=158; $i < 164; $i++)     { &sd_talk(&sd_get_lang($i)); }
	&sd_talk("/sdset slots #1 #2");
        for (my $i=164; $i < 167; $i++)     { &sd_talk(&sd_get_lang($i)); }
	&sd_talk("/sdset que #1");
        for (my $i=167; $i < 169; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdgetinfo")
	{
        &sd_talk(&sd_get_lang(169));
        &sd_talk(&sd_get_lang(135));
        for (my $i=170; $i < 173; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdshowword")
	{
        &sd_talk(&sd_get_lang(173));
	&sd_talk(&sd_get_lang(82));
        for (my $i=174; $i < 176; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdshowskin")
	{
        &sd_talk("/sdshowskin #");
	&sd_talk(&sd_get_lang(82));
        for (my $i=176; $i < 183; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdcontrolsend")
	{
        for (my $i=386; $i < 417; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdsend")
	{
        &sd_talk("/sdsend #");
        for (my $i=431; $i < 434; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sduserinfo")
	{
        for (my $i=434; $i < 437; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdclearusers")
	{
        &sd_talk("/sdclearusers");
        for (my $i=189; $i < 191; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdstopsend")
	{
        &sd_talk("/sdstopsend");
        for (my $i=437; $i < 439; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sdcustomads")
	{
        for (my $i=183; $i < 187; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}
if ($line eq "sduptime")
	{
        for (my $i=187; $i < 189; $i++)     { &sd_talk(&sd_get_lang($i)); }
	}

return 1;

}

sub sd_show_custom_ads
# display the custom ads to all serving channel
{
if ($customAdsEnable eq "true")
  {
  &sd_create_skin("5 $SDLang");
  for (my $i=0; $i < @SkinMaker; $i++)    { &sd_to_serving_channel(@SkinMaker[$i]); }
  IRC::add_timeout_handler($customAds,"sd_show_custom_ads");
  }
}

sub sd_custom_ads
# on enter ON or OFF
{
my $line=lc(shift(@_));
if ($line eq "on")
        {
        if ($customAdsEnable eq "false")
                {
                sd_talk(&sd_get_lang(467));
                $customAdsEnable="true";
                sd_show_custom_ads;
                }
        }
if ($line eq "off")
        {
        $customAdsEnable="false";
        sd_talk(&sd_get_lang(468));
        }
return 1;
}

sub formatFilename
# on enter @_ is the filename
# on exit the filename with _ convert to space if needed
{
my $orig=shift(@_);
my $new=join " ", split("_",$orig);
if ($noUnderscore eq "true")    { $orig = $new; }
return $orig;
}

sub sd_StopSend
# just set $forceSendCancel to true to stops all current sends
{
$forceSendCancel="true";
sd_talk(&sd_get_lang(426));
return 1;
}

sub sd_build_list
{
if ($buildingList==0)
        {
        $backServerEcho=$ServerEcho;
        &sd_talk(&sd_get_lang(216)." $mainDir...");
        $buildingList=1;
        open(LISTOUT, ">".$mlist1);      # build file to export values of our list
        # We will write out everything we need to build our list...
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
	$year += 1900;
	$mon++;
	$listDate=$mday." ".$mon." ".$year;
        # v2.0 now takes language dependant format of date so we store that date in a format
        # that feet better the sd_format_date subroutine
        $listDateM=$mon;
        $listDateY=$year;
        $listDateD=$mday;
	if ($firstDate eq "none")	{ $firstDate=$listDate; }		# saved our first time use!
        # again use of language dependant date
        $newList=$Trigger."-".sd_format_date($listDate." ".@LangTable[80+$dispLang]);
        print LISTOUT "$mainDir;;;;";
        print LISTOUT "$useMOTD;;;;";
        print LISTOUT "$MOTDfile;;;;";
        print LISTOUT "$followLink;;;;";
        print LISTOUT "$Trigger;;;;";
        print LISTOUT "$useZIP;;;;";
        print LISTOUT "$oldList;;;;";
        print LISTOUT "$newList;;;;";
        print LISTOUT "$noUnderscore;;;;";
        print LISTOUT "$listShowSize;;;;";
        print LISTOUT "$listChars;;;;";
        print LISTOUT sd_get_lang(218).";;;;";
        print LISTOUT sd_get_lang(219).";;;;";
        print LISTOUT sd_get_lang(221).";;;;";
        close(LISTOUT);
        my $runPerl="perl ".$myhomepath."SDUtil.pl";
        IRC::command("/EXEC -o $runPerl");
        &sd_check_list_ready;
        }
else    { &sd_talk(&sd_get_lang(268)); }
return 1;
}

sub sd_load_internalList
# This function load our internal list in order to accept requests and make @find works
{
$refList=$Trigger."-list.ref";
while (@internalList > 0)       { pop @internalList; } # clear it...
if (open(fileOut,"<".$myhomepath.$refList))
                {
                while (!eof(fileOut))   { push @internalList, <fileOut>; }
                my $count = @internalList;
                for (my $kkk=0; $kkk < $count; $kkk++)
                        {
	                chomp($internalList[$kkk]);
                        chomp($internalList[$kkk]);
                        }
                close(fileOut);
                }
        else    { sd_talk("BAD LIST..."); }
}

sub sd_check_list_ready
# check if our list is ready or still working on it
{
if (open(LISTIN,"<".$mlist2))
        {
        $ServerEcho=$BackServerEcho;
        my $list=<LISTIN>;
        my @load=split(";;;;",$list);
        close(LISTIN);
        $newList=$load[0];
        $oldList=$load[1];
        $zipList=$load[2];
        $L_res1=$load[3];
        $L_res2=$load[4];
        $totalFiles=$load[6];
        $totalDir=$load[7];
        $totalKB=$load[8];
        &sd_talk(&sd_get_lang(217).": $load[5] ".&sd_get_lang(2));
        &sd_talk(&sd_get_lang(220).": $load[9] ".&sd_get_lang(2));
	&sd_talk("$totalDir ".&sd_get_lang(222));
	&sd_talk(&sd_get_lang(223)." $L_res1 ".&sd_get_lang(224)." ($L_res2 ".&sd_get_lang(225).")");
	my $upsize = &sd_byte_converter($totalKB);
	&sd_talk("$totalKB ".&sd_get_lang(226)." ($upsize)");
	&sd_talk(&sd_get_lang(227)." : ".$myhomepath.$oldList);
	&sd_talk(&sd_get_lang(228)." - ".&sd_get_lang(10));
	if ($useZIP eq "true")
			{
			&sd_talk(&sd_get_lang(229)." : ".$load[10]);
			$newList=$zipList;
			}
	&sd_talk(&sd_get_lang(230)." : $newList");
        @sdtemp=`rm $mlist2`;   # kicking out working status
        # we must load now the @internalList with values...
        &sd_load_internalList;
        $buildingList=0; # unarm flag
        &sd_save_cfg;
        $ServerEcho=$BackServerEcho;
        }
  else  {
        IRC::add_timeout_handler(1000,"sd_check_list_ready");	# recheck every second
        }
}

# output to ourself
sub sd_talk
{
# my $server=IRC::get_info(3);
my $line=shift(@_);
#IRC::print_with_channel("$line ", $sdtab, $server);
IRC::print_with_channel("$line ", $sdtab, ""); # xchat doc say $server is optional :)
if ($ServerEcho eq true)        { push @sd_buffer, "/MSG $ServerNick"." -> "."$line"; }
}

# output to a user or channel
sub sd_message
{
my $line = shift(@_);
if ($serverEcho eq "true")      { &sd_talk(&sd_get_lang(232)." : ".$line); }
push @sd_buffer, "/MSG $line";
}

# notice ot channel or user
sub sd_notice
{
my $line = shift(@_);
push @sd_buffer, "/notice $line";
}

# Search through files for the filename & return the correct file with path
sub sd_get_file
{
my $match0 = "@_";				# 0 - Search real pattern
chomp($match0);
my $match1 = join(' ',split('_',"$match0"));	# 1 - Search pattern with spaces
my $match2 = join('_',split(' ',"$match0"));	# 2 - Search pattern, but with space as _ (to help spr jukebox resume)
my $match3 = lc($match0);			# 3 - Search #0 but in lowercase
my $match4 = lc($match1);			# 4 - Search #1 but in lowercase
my $match5 = lc($match2);			# 5 - Search #2 but in lowercase
my $seek = @internalList;
my $gotFile = 0;
@file2send[0]="none";
for (my $i=0; $i < $seek; $i++)
	{
	$ffText=@internalList[$i];
	chomp($ffText);
	$ffPath=@internalList[$i+1];
	$ffSize=@internalList[$i+2];
	$i=$i+2;
	for (my $k=0; $k < 6; $k++)
		{
		if ($k == 0) { $match = $match0; $cfText=$ffText; }
		if ($k == 1) { $match = $match1; $cfText=$ffText; }
		if ($k == 2) { $match = $match2; $cfText=$ffText; }
		if ($k == 3) { $match = $match3; $cfText=lc($ffText); }
		if ($k == 4) { $match = $match4; $cfText=lc($ffText); }
		if ($k == 5) { $match = $match5; $cfText=lc($ffText); }
		$ffs = length($cfText);
		$ffm = length($match);
		if ($ffs <= $ffm)
				{
				$cmatch = substr $match, 0, $ffs;
				if ($cmatch eq $cfText)
					{
					@file2send[0] = $ffText;		# Store real name
					@file2send[1] = $ffPath;		# Store pathname
					@file2send[2] = $ffSize;		# Store size (for stats)
					$gotFile = 1;
					$k = 6;
					$i = $seek;
					}
				}
		}
	} # $i loop
}

sub sd_analyse
{
	my $line = shift;
	my $backLine=$line;
	my $goodTrigger = false;
        &sd_update_hostname($backLine);
	$line =~ /:.*?\s*([^!]*)!.*? +PRIVMSG +(.*?)\s*:(.*)/;
	my $nick = $1;
	my $chan = $2;
	my $msg = $3;
	my $me=IRC::get_info(1);
	my $cc = IRC::get_info(2);
	my $irc_server= IRC::get_info(3);
	@file2send[3]=$nick;
	@file2send[4]=$chan;
	@file2send[5]=IRC::get_info(3);
	if (lc($Trigger) eq lc(substr($msg,1,length($Trigger))))		# check our trigger
		{
                # Found a bug here, not so harmful
                # Detection is base on our nick by comparing it with what a user is saying.
                # Check is done on our nick length
                # So a user saying in a channel : @bob-another-help
                # If your nick is "bob", then the script will think that user is talking to you:
                # 'cause: bob is compare to @bob-another-help
                #         ^^^                ^^^
                # So correcting it with a fix like removing the last "-help" (last -?????) and "@" and
                # do a strict compare will introduce a new one
                # works : bob is compare to @bob-another-help <- strict compare of result without last -??????
                #         ^^^                ^^^^^^^^^^^
                # But what happen if user request the list ? @bob-another
                # bug   : bob is compare to @bob-another <- -another has been removed...
                #         ^^^                ^^^
                # Even fixing it, by comparing what user as said with our nick+all possibilities of true
                # commands (like @bob, @bob-help, @bob-remove...) will not correct the bug if another user
                # got a nick like "bob-help", "bob-remove"...
                # As SDClone commands are pass as SDFind & SpRJukebox, i suppose they didn't think about
                # that bug when they create their scripts... So WHY SHOULD I TAKE CARE ?
                # It could be funny to see a user "bob" and another one "bob-remove" in the same channel :)
                # Everytime a user request list from "bob-remove", it remove all files for him from "bob"'s que
		$goodTrigger = "true";
		$SDCheck = lc($Trigger);
		}
	if (lc($me) eq lc(substr($msg,1,length($me))))		# check our name as trigger
		{
		$goodTrigger = "true";
		$SDCheck = lc($me);
		}
        if (($goodTrigger eq "true") && ($chan eq $me)) # v1.1 change for the test-> $chan ne $cc
		        {
		        $msg = "nothing";    # invalid because not asking in a channel
		        $goodTrigger="false";
		        sd_message($nick." $nick ".&sd_get_lang_user("233 $nick"));
		        }
        my $checkLame=sd_user_isban($nick);
        if ((("!seen ") eq (lc(substr($msg,0,6)))) && ($useSeen eq "true"))
                {
                &sd_talk("$nick: $msg");
                @SDcmdList = split ' ', $msg, 2;
                &sd_try_seen($nick." ".@SDcmdList[1]." ".$chan);
                }
	if (lc("$findCmd ") eq lc(substr($msg,0,length($findCmd)+1)))
		{
                &sd_talk("$nick: $msg");
		if ($checkLame ne "true") # added in v1.1, avoid answer to find request if user is ban
		        {
		        @SDcmdList = split ' ', $msg, 2;
		        if (($allowFind eq "true") && ($SDWorking > 1))
				{
                                my $savedUserTracking="";
                                if ($userTrackingOnFind ne "true")
                                        { # Don't track user on @find
                                        $savedUserTracking=$userTracking;
                                        $userTracking="false";
                                        }
                                &sd_update_query($nick);       # update user stats
                                my $doBan="false";
                                if (((@SDcmdList[1] ne "*.*") && ($autoBan_ext eq "true")) && (substr(@SDcmdList[1],0,2) eq "*."))
                                        {
                                        $doBan="true";
                                        }
                                if ((@SDcmdList[1] eq "*.*") && ($autoBan_all eq "true"))
                                        {
                                        $doBan="true";
                                        }
				if ($doBan ne "true")	{ &sd_find_request("$nick @SDcmdList[1]"); }
						else    {
						        &sd_message($chan." $nick ".&sd_get_lang_user("234 $nick"));
						        if ($autoBan eq "true") { &sd_ban($nick." ".$msg); }
						        }
                                if ($userTrackingOnFind ne "true")
                                        { # Restore our state
                                        $userTracking=$savedUserTracking;
                                        }
                                }
                        }
		}
              if (($goodTrigger eq "true") && ($checkLame eq "true"))
		        {
                        sd_talk("$nick: $msg");
                        sd_talk(&sd_get_lang(469));
		        sd_notice("$nick ".&sd_get_lang_user("235 $nick"));
		        $goodTrigger="false";
		        }
                if (($SDWorking < 2) && ($goodTrigger eq "true"))
		        {
                        sd_talk("$nick: $msg");
		        sd_notice("$nick ".&sd_get_lang_user("236 $nick"));
		        $goodTrigger = "false";     # invalidate the request
	                      }
	if ($goodTrigger eq "true")				# we found our nick or trigger in it, so it's for us
		{
		&sd_talk("$nick: $msg");
                &sd_update_query($nick);      # update user stats
		@SDcmdList = split ' ', $msg, 2;
		if (length(@SDcmdList) >= 0)
			{						# we have a command
			$SDCmdTag = lc(@SDcmdList[0]);			# get the cmd in lowercase
			$SDFileTag = @SDcmdList[1];			# store filename if any
			if ($SDCmdTag eq '@'.+$SDCheck.+"-help")	# get help
				{
                                for (my $ttt=237; $ttt < 244; $ttt++)
                                    { &sd_message("$nick ".&sd_get_lang_user("$ttt $nick")); }    # output help
                                if ($userTracking eq "true")
                                    {
                                    for (my $ttt=244; $ttt < 247; $ttt++)
                                        { &sd_message("$nick ".&sd_get_lang_user("$ttt $nick")); }    # output help
                                    }
                                for (my $ttt=247; $ttt < 252; $ttt++)
                                    { &sd_message("$nick ".&sd_get_lang_user("$ttt $nick")); }    # output help

                                if ($useSeen eq "true") { &sd_message("$nick ".&sd_get_lang_user("486 $nick")); }

                                for (my $ttt=252; $ttt < 253; $ttt++)
                                    { &sd_message("$nick ".&sd_get_lang_user("$ttt $nick")); }    # output help
                                }
			if ($SDCmdTag eq '@'.+$SDCheck.+"-que")
				{
				&sd_nick_view_que($nick);
				}
			if ($SDCmdTag eq '@'.+$SDCheck.+"-stats")
				{
				&sd_check_stats($nick);
				}
                        if ($SDCmdTag eq '@'.+$SDCheck.+"-leech")
                                {
                                if ($userTracking ne "true")
                                      { &sd_message("$nick ".&sd_get_lang(423)); }
                                else  { &sd_User_Getleech($nick); }
                                }
                        if ($SDCmdTag eq '@'.+$SDCheck.+"-language")
                                {
                                if ($userTracking ne "true")
                                        { &sd_message("$nick ".&sd_get_lang(423)); }
                                else
                                  {
                                  if ($SDFileTag eq "")
                                           {
                                           &sd_message("$nick ".&sd_get_lang_user("417 $nick"));
                                           for (my $ipx=0; $ipx < $nbLang; $ipx++)
                                                {
                                                &sd_message($nick." $SDcolorh".$LangIndex[$ipx]."$SDcolor - ".$LangInfo[$ipx*4+1]);
                                                }
                                           }
                                  else    {
                                          &sd_change_user_language("$nick $SDFileTag");
                                          }
                                  }
                                }
			if ($SDCmdTag eq '@'.+$SDCheck.+"-remove")
				{
				if ($SDFileTag eq "")
					{
					&sd_que_killer("$nick");
					}
				else	{
					&sd_que_killer("$nick $SDFileTag");
					}
				}
			if ($SDCmdTag eq '@'.+$SDCheck.+"-top")
				{
				sd_notice("$nick ".&sd_get_lang_user("253 $nick"));
				}
			if ($SDCmdTag eq '@'.+$SDCheck.+"-server")
				{
				sd_notice("$nick ".&sd_get_lang_user("254 $nick")." $irc_server");
				}

			if ($SDCmdTag eq '!'.+$SDCheck.+"-slots")
				{
				&sd_show_slots($nick);
				}
			if ($SDCmdTag eq '@'.+$SDCheck.+"-slots")       # added to help users
				{
				&sd_show_slots($nick);
				}
                        if ($SDCmdTag eq '@'.+$SDCheck)						# get list
				{
				if ($buildingList == 0)
					{
					if (($CPSsendfile + $CPSwaitfile) < ($otherFile + $slotMax))
					        {
					        IRC::command("/dcc send $nick $myhomepath".$newList);
					        sd_notice("$nick ".&sd_get_lang_user("255 $nick"));
					        }
					else    {
					        sd_notice("$nick ".&sd_get_lang_user("256 $nick"));
					        }
					}
				else	{
					sd_notice("$nick ".&sd_get_lang_user("257 $nick"));
					}
				}
			if ($SDCmdTag eq '!'.+$SDCheck)							# get a file
				{
                                &sd_update_user_info("$nick;;;;file;;;;nothing;;;;");
                                &sd_get_file($SDFileTag);
				if (@file2send[0] eq "none")
					  {
                                          sd_talk("$SDFileTag ".&sd_get_lang(470));
					  sd_notice("$nick ".&sd_get_lang_user("11 $nick")." $SDFileTag !");
					  }
				else      {
				          &sd_add_que;
				          if ($SDWorking == 2) { sd_notice("$nick ".&sd_get_lang_user("258 $nick")); }
				          }
				}
			if ($SDCmdTag eq '!'.$SDCheck."-info")			# get a file info
				{
				&sd_get_file_information("$nick $SDFileTag");
				}

			}
		return 1;
		}
	else {
	        my @ctcp = split " ",lc($line);     # implement a version reply
	        if (lc(@ctcp[3]) eq ":\001version\001")
		        { sd_notice($nick." \001VERSION SDClone $Ver for Linux (http://sdclone.openbsd.cx) by $Author\001"); }
	        return 0;
	        }
}

sub sd_update_query
# just update user stats
# on enter: nick
{
if ($userTracking eq "true")
   {
   my $line=lc(shift(@_));
   my $forcetest=&sd_get_user_lang_pref($line);    # This just assure that this function is call 1st
                                                   # To create the user file stats
   &sd_update_user_info("$line;;;;query;;;;nothing;;;;");
   }
}

sub sd_get_file_information
# return information about a file
# on enter Nick + File
# on exit nothing, send info to Nick about File
{
my $line=shift(@_);
my @ss=split " ",$line,2;
my $SDFileTag=@ss[1];
my $nick=@ss[0];
sd_get_file($SDFileTag);
if (@file2send[0] eq "none")
          {
          sd_notice("$nick ".&sd_get_lang_user("11 $nick")." $SDFileTag !");
          }
   else   {
          # info filename is @file2send[0], path is [1], size is [2]
          my $fginfo = '"'.@file2send[1].'"';
          my $infoget = `file $fginfo`;      # take info from file linux cmd
          my @finf=split ":",$infoget,0;     # parse info taken from file
          $infoget=@finf[1];                 # now we have the info into $infoget
          sd_notice("$nick ".&sd_get_lang_user("259 $nick")." ".@file2send[0]);
          $fginfo=sd_byte_converter(@file2send[2]);
          &sd_notice("$nick ".&sd_get_lang_user("260 $nick")." : ".@file2send[2]." ".&sd_get_lang_user("261 $nick")." (".$fginfo.")");
	  &sd_notice("$nick ".&sd_get_lang_user("262 $nick")." : $infoget");
	  }
}

sub sd_friend_que
# Use to load a file with filenames in it and multi-add that files to your que for that nick
# To use it /sdfriend Nick Filename
# The file must be made with filenames present line by line
# file1
# file...
# last file to add
{
my $line = shift(@_);
my @actions=split " ",$line;
my @files=();
if (@actions == 2)
	{
	sd_talk(&sd_get_lang(263)." ".@actions[0]);
	$line = @actions[1];
		if (open(fileOut,"<".$line))
			{
                        while (!eof(fileOut))   { push @files, <fileOut>; }
                        close(fileOut);
                        }
		else
			{
			sd_talk(&sd_get_lang(8)." ".@actions[1]);
			}
        my $count = @files;
	&sd_notice(@actions[0]." ".&sd_get_lang_user("264 ".@actions[0]));
	&sd_notice(@actions[0]." ".&sd_get_lang_user("265 ".@actions[0]));
        my $spd1=&sd_get_lang(266);
        my $spd2=&sd_get_lang(11);
        my $spd3=&sd_get_lang(267);
        for (my $kkk=0; $kkk < $count; $kkk++)
		{
                my $temp1=@files[$kkk]; # filename
                chomp($temp1);
                chomp($temp1);
                my $temp2=$temp1;
                if (lc($Trigger) eq lc(substr($temp1,1,length($Trigger))))
                        { # trigger is in it
                        my @tempTab=split ' ',$temp1,2;
                        $temp2=@tempTab[1];     # get the filename
                        }
		&sd_talk($spd1." : ".$temp2);
		&sd_get_file($temp2);
		if (@file2send[0] eq "none")
		        {
			sd_talk("--- ".$spd2." ".$temp2);
			}
		else
		        {
			($f_name,$f_path,$f_fsize,$f_nick,$f_chan,$f_server) = @file2send;
			$f_nick=@actions[0];
			push @queFile, $f_name;
			push @quePath, $f_path;
			push @queFSize, $f_fsize;
			push @queUser, $f_nick;
			push @queChan, $f_chan;
			push @queServer, $f_server;
			push @queWait, time;
                        push @queIdent, "hostname"; # forcing hostname to bypass ident
			$f_pos=@queFile;	# get position in que
			&sd_talk($spd3." $f_pos --> $f_nick");
			}
                }
	}
return 1;
}

sub sd_load_motd
# Use to load a file to use as MOTD
# \r\n is add to everylines to help windows reading
{
while (@MOTD > 0)  { pop @MOTD; }     # clear the table
if (open(fileOut,"<".$myhomepath.$MOTDfile))
        {
        while (!eof(fileOut))   { push @MOTD, <fileOut>; }
        my $count = @MOTD;
        for (my $kkk=0; $kkk < $count; $kkk++)
                {
	  chomp(@MOTD[$kkk]);
	  chomp(@MOTD[$kkk]);
	  @MOTD[$kkk]=@MOTD[$kkk]."\r\n";
                }
        close(fileOut);
        sd_talk(&sd_get_lang(268)." - ".&sd_get_lang(9));
        }
else
        {
        sd_talk(&sd_get_lang(268)." - ".&sd_get_lang(8)." $myhomepath".$MOTDfile);
        }
}

sub sd_que_count_users
# count number of users in the que
# also count number of actives users & actives files in que (users that are present in a serving channel)
{
my $count = @queUser;
my @sUsers= @queUser;
my $ccNick = @queUser[0];
my $sTest="false";
my $retValue = 0;
$filesActive=0;
$usersActive=0;
for (my $k=0; $k < $count; $k++)
	{
	$ccNick = lc(@sUsers[$k]);
        if ($ccNick ne " ")
                	{
                        $sTest=&sd_user_visible($ccNick);
                        if ($sTest eq "true")   { $usersActive++; } # that user is present & active
			for (my $i=$k; $i < $count; $i++)
			        {
			        if ($ccNick eq lc(@sUsers[$i]))	{       # another file from same user
                                                                @sUsers[$i]=" ";
                                                                if ($sTest eq "true") { $filesActive++; }
                                                                }
   			        }
			$retValue++;
			}
	}
return $retValue;
}

sub sd_get_uptime
# just return formatted uptime
{
# Using UP, nicer output.
my $myup="";
if ($openBSD ne "true") { $myup = `up`; }
                else    { $myup = ""; }
        my @linux=();
        if ($myup eq "")
		{						# using UPTIME if fail
		$myup = `uptime`;
                my $hours="";
                my $mins="";
		my @days = split 'users,', $myup;
                @linux = split 'up ', @days[0];
		@days = split ':',@linux[1];
                $hours=@days[0];
                $mins=@days[1];
                @linux=split ',',$hours;
                $myup=$linux[0]; # start rebuilding infos
                if ($linux[1] > 1)      { $myup.=$linux[1]." hours"; }
                                else    { $myup.=$linux[1]." hour"; }
                @linux=split ',',$mins;
                if ($linux[0] > 1)      { $myup.=" ".$linux[0]." minutes"; }
                                else    { $myup.=" ".$linux[0]." minute"; }

		}
	else	{
		@linux = split 'up ',$myup;
		$myup=@linux[1];
		}
return $myup;
}

sub sd_check_stats
# sends our stats to a user
{
my $nick = shift(@_);
&sd_get_lang_Index(&sd_get_user_lang_pref($nick));
&sd_create_skin(2);     # create the skin
for (my $i=0; $i < @SkinMaker; $i++)    { &sd_message("$nick ".@SkinMaker[$i]); }

return 1;
}

sub sd_check_add_que
# check to see if file is already in que
# on enter $nick & $filename
# on exit
# $check_nick_free_que = number of files nick got in que
# $check_nick_file_que = position in que
{
$line = shift(@_);
my @myparam = split ";;;;",$line;
($nick,$file) = @myparam;
my $count = @queUser;	# size of que
$check_nick_free_que = 0;
$check_nick_file_que = -1;
for (my $k = 0; $k < $count; $k++)
	{
	if (lc($nick) eq lc(@queUser[$k]))
		{
		$check_nick_free_que++;
		if ($file eq @quePath[$k])	{ $check_nick_file_que = $k; }
		}
	}
}

sub sd_check_que_cheat
# check if an ident is in que with 2 differents nick and they are active (waiting file)
# so they can bypass que limit
# on enter Host
# on exit nothing
{
my $count=@queUser;
my $nick1="";
my $nick2="";
my $ident1="";
my $ident2=lc(shift(@_));
my $active1="false";
my $active2="false";
my $checked="false";
my $spd1=&sd_get_lang(471); # sd_get_lang is a cpu cost function, by setting a var with the result
my $spd2=&sd_get_lang(472); # we limit call to that function in a loop to get the same result...
my $spd3=&sd_get_lang(473);
my $spd4=&sd_get_lang(474);
my $spd5=&sd_get_lang(475);
my @queCtrl=();
for (my $i = 0; $i < $count; $i++)      # loop our que
        {
        $nick1=@queUser[$i];
        $ident1=lc(@queIdent[$i]);
        if (($ident1 eq $ident2) && ($ident1 ne "hostname"))
                {       # same ident
                my $checked="true";
                for (my $j=0; $j < @queCtrl; $j++)
                        {
                        if ($nick1 eq $queCtrl[$j])     { $checked="false"; }
                        }
                if ($checked eq "true") { push @queCtrl, $nick1; }
                }
        }
# Now we have all nicks with same ident in @queCtrl table
# Just check that at least 2 are active
$checked="false";       # set it to false
for (my $j=0; $j < @queCtrl; $j++)
        {
        $active1=&sd_user_visible($queCtrl[$j]);
        if ($active1 eq "true")         # user is present
                {
                if ($checked eq "false")   { $checked=$queCtrl[$j]; } # Set $checked so now at least one user is present
                        else               { # already checked, so at least two users are present
                                           &sd_talk($spd1." ($ident1) ".$spd2." $checked ".$spd3." $queCtrl[$j]");
                                           &sd_talk($spd4);
                                           if (($autoBan eq "true") && ($autoBan_DNS eq "true"))
                                                        { &sd_ban("$checked ".$spd5." $queCtrl[$j]"); }
                                           $j=@queCtrl; # force exit of loop
                                           }
                }
        }
}

sub sd_view_que
{
my $line = lc(shift(@_));
my $count = @queUser;
&sd_talk(&sd_get_lang(269));
my $spd1=&sd_get_lang(270);
my $spd2=&sd_get_lang(271);
for ($k = 0; $k < $count; $k++)
	{
	my $ww = sd_comp_time(@queWait[$k]);
	if ($line eq "")
		{
		&sd_talk("$k - ".@queUser[$k]." ".$spd1.": ".@queFile[$k]." - ".$spd2.": $ww");
                #sd_talk("$k - ".@queUser[$k]." - ".@queIdent[$k]);
		}
	else	{
		if ($line eq lc(@queUser[$k]))
			{
			&sd_talk("$k - ".@queUser[$k]." ".$spd1.": ".@queFile[$k]." - ".$spd2.": $ww");
                        #sd_talk("$k - ".@queUser[$k]." - ".@queIdent[$k]);
			}
		}
	}
return 1;
}

sub sd_loop_timer
# just a function to be called regulary
{
&sd_check_slots;
# IRC::add_timeout_handler($checkTimer,"sd_loop_timer");
# Disable and move to sd_que_control to avoid re-run it while we haven't finish working on que
}

sub sd_show_uptime
# just show your uptime
{
&sd_to_serving_channel(&sd_get_lang(482));
return 1;
}

sub sd_check_buffer
# output what it's in the buffer every 1 s and remove it
{
$dailyTimer++;          # add one seconds to timer
# the daily timer is use to get a day : 60s*60m*24h = 1000*60*60*24 = 86400000
# but this function is called every seconds, so 60*60*24 = 86400 for a day
$bufferTimerSlice++;
$statsSave++;
if ($statsSave >  1800)
        {
        &sd_save_cfg;
        if ($tf_fastSeverity > 0)
             {
             $tf_fastSeverity--;   # Lower target change too fast severity
             &sd_talk("NOTE: Lowering target change to fast severity to $tf_fastSeverity");
             if ($tf_fastSeverity  > 4)  { $bufferTimer=($tf_fastSeverity-5)+4; }
             if ($tf_fastSeverity == 4)  { $allowFind=$tf_findStatus; }
             if ($tf_fastSeverity == 3)  { $maxFind=1; }
             if ($tf_fastSeverity == 2)  { $maxFind=3; }
             if ($tf_fastSeverity == 1)  { $bufferTimer=4; }
             if ($tf_fastSeverity == 0)  { $bufferTimer=$tf_BuffTimer; }
             }
        $statsSave=0;
        }
if ($dailyTimer > 86400)
        {
        if ($dailyReport eq "true")
	        {
                &sd_get_lang_Index($SDLang);
                &sd_create_skin(3);       # Create the skin
                for (my $i=0; $i < @SkinMaker; $i++)    { &sd_to_serving_channel(@SkinMaker[$i]); }
                }
        $cpsRecordDay=0;
        $cpsRecordNickDay=&sd_get_lang(0);
        &sd_talk(&sd_get_lang(272));
        $dailyTimer=0;
        }
my $tmpout = "";
if ($bufferTimerSlice > $bufferTimer)
        {
        $bufferTimerSlice = 1;
        if (@sd_buffer > 0)
                {
                $tmpout = shift (@sd_buffer);
                &sd_logging($tmpout);
                &sd_dump_buffer($tmpout);
                IRC::command($tmpout);
                }
        }
my $qqc=@queReqCount;
my $toq=0;
for (my $stque=0; $stque < $qqc; $stque++)
        {
        $toq=@queReqCount[$stque];
        if ($toq > 1)     {
	                 $toq--;
                               @queReqCount[$stque]=$toq;
                                }
	        else   { &sd_remove_queRequest(@queReqUser[$stque]); }
         }
IRC::add_timeout_handler(1000,"sd_check_buffer");
}

sub sd_user_visible
# check if user is still visible to us
# if not, we will cancel the send
# on enter nick to test
# on exit status, true user is here, Server[0] if user is no more in a serving channel
{
my $checkUser = lc(shift(@_));
my @channels=IRC::channel_list();	# get info on all channels
my $chanCount=@channels;
my @infoServ=();
my @infoChan=();
for (my $i=0; $i < $chanCount; $i++)
	{
	if (@channels[$i] ne @channels[$i+1])
		{
		push @infoChan, @channels[$i];		# store channels where we are for easier use
		push @infoServ, @channels[$i+1];	# these infos will be use to identify the nick that will received the file
		# i drop our nick info
		}
	$i=$i+2;
	}
my $seenHim="false";
for (my $kk=0; $kk < $chanCount; $kk++)
        {       # loop channels where we are
        my $servCount=@chans;   # channels where we serve files
        for (my $jj=0; $jj < $servCount; $jj++)
                {
                if (lc(@infoChan[$kk]) eq lc(@chans[$jj]))      # we found a channel where we are & serving
                        {
                        my $test=&sd_is_nick_here($checkUser.";;;;".@chans[$jj].";;;;".@infoServ[$kk]);
                        if ($test eq "true")    { $seenHim="true"; }
                        }
                } # @chans loop
        } #@infoChan loop
if ($seenHim eq "false")  { $seenHim=@infoServ[0]; } # we don't know where he is, so assume 1st server
return $seenHim;
}

sub sd_get_file_complete
# return % of a file upload/download completion
# on enter: file position (dcc_resume) and file size (dcc_size)
# on exit : 0%->100%
# Currenlty disable -> doesn't works under xchat 1.8.9 look next function for details
{
my $line=shift(@_);
my ($dc_re, $dc_si)=split " ",$line;
my $dc_percent=($dc_re * 100) / $dc_si;
return $dc_percent."%";
}

sub sd_get_file_eta
# return the estimate waiting time to finish a file send/get
# on enter: file position (dcc_resume), file size (dcc_size), file speed (dcc_cps)
# on exit : ETA as xx (hour, min or sec) or 0 sec
# Currently that function is disable as it will not work under xchat 1.8.9
# The only way to enable it, is a patch to xchat/src/common/perl.c
# find : XST_mIV (i, dcc->pos);
# replace by :   {
#                if (dcc->dccstat == STAT_ACTIVE)
#                        XST_mIV (i, dcc->ack);
#                 else   XST_mIV (i, dcc->pos);
#                }
{
my $line=shift(@_);
my ($dc_re, $dc_si, $dc_cp)=split " ",$line;
my $dc_ETA="0 sec";
if ($dc_cp > 0) {
                $dc_eta=($dc_si-$dc_re) / $dc_cp;
                # Perl doc
                # You should not use this function for rounding: one because it truncates towards 0,
                # and two because machine representations of floating point numbers can sometimes produce
                # counterintuitive results.
                # eheh exactly what i need !
                $dc_hh=int($dc_eta / 3600);
                $dc_mm=($dc_eta / 60) % 60;
                $dc_ss=$dc_eta % 60;
                if ($dc_hh > 0)  {
                                 $dc_ETA=$dc_hh;
                                 if ($dc_hh > 1)        { $dc_ETA.="hours"; }
                                        else            { $dc_ETA.="hour"; }
                                 }
                        else     {
                                 if ($dc_mm > 0)    {
                                                    $dc_ETA=$dc_mm;
                                                    if ($dc_mm > 1) { $dc_ETA.="mins"; }
                                                            else    { $dc_ETA.="min"; }
                                                    }
                                        else  {
                                              if ($dc_ss > 0)    {
                                                                $dc_ETA=$dc_ss;
                                                                if ($dc_ss > 1) { $dc_ETA.="secs"; }
                                                                        else    { $dc_ETA.+"sec"; }
                                                                }
                                              }
                                 }
                }
return $dc_ETA;
}

sub sd_check_slots
# here we open slots to add a new send
#    XST_mPV(i, dcc->nick);
#    XST_mPV(i, dcc->file); looks like filename when receiving a file, filename+path when sending
#    XST_mIV(i, dcc->type); looks like 1=received, 0= send <- now i'm sure it's good
#    XST_mIV(i, dcc->stat); maybe status: 0-waiting, 1-active, 2-finish, 3-echec, 5-stopped (0 & 1 & 2 are sure)
#            v2.0 note      Sending: 0-waiting,
#            v2.0 note      Receiving: 0-waiting, 1-active, 2- fail, 3- finish
# v2.0 note: /dcc close get nick file <- clear from dcclist that file
# v2.0 note: /dcc close send nick file <- clear from dcclist that file
#    XST_mIV(i, dcc->cps);
#    XST_mIV(i, dcc->size);
#    XST_mIV(i, dcc->resumable);
#    XST_mIV(i, dcc->addr);
#    XST_mPV(i, dcc->destfile); looks like none when sending a file, filename+path we receiving
# new from dcc.c == dcc->cps = (dcc->ack - dcc->resumable) / sec;
{
@dcclist=IRC::dcc_list();
$dcclength=@dcclist;
my $dc_ni="";
my $dc_fi="";
my $dc_ty="";
my $dc_st="";
my $dc_cp="";
my $dc_si="";
my $dc_re="";
my $dc_ad="";
my $dc_de="";
my $me=IRC::get_info(1);
while (@dcc_nick > 0)
	{
	pop @dcc_nick;
	pop @dcc_file;
	pop @dcc_cps;
	pop @dcc_size;
	}
$CPSgetfile=0;
$CPSsendfile=0;
$CPSwaitfile=0;
$CPSget=0;
$CPSsend=0;
for (my $i=0; $i < $dcclength; $i++)
	{
	$dc_ni = @dcclist[$i];
	$dc_fi = @dcclist[$i+1];
	$dc_ty = @dcclist[$i+2];
	$dc_st = @dcclist[$i+3];
	$dc_cp = @dcclist[$i+4];
	$dc_si = @dcclist[$i+5];
	$dc_re = @dcclist[$i+6];
	$dc_ad = @dcclist[$i+7];
	$dc_de = @dcclist[$i+8];
#        my $dc_percent=&sd_get_file_complete("$dc_re $dc_si");
#        my $dc_eta=&sd_get_file_eta("$dc_re $dc_si $dc_cp");
#        &sd_debug("perc : $dc_percent");
#        &sd_debug("ETA  : $dc_eta");
	if (($dc_ty == 1) && ($dc_st == 1))		# we are receiving a file
		{
		$CPSgetfile++;
		$CPSget=$CPSget+$dc_cp;
		}
        if (($dc_ty == 1) && ($dc_st == 3))     # finish getting a file, just thank the user
                {
                &sd_notice($dc_ni." Thank you for ".$SDcolorh.$dc_fi." !");
                &sd_out_download("$dc_ni - $dc_fi");
                IRC::command("/dcc close get $dc_ni $dc_fi");
                }
        if (($dc_ty == 0) && ($dc_st == 2))
                {       # just remove entry on send fail... (user cancel)
                IRC::command("/dcc close send $dc_ni $dc_fi");
                }
        if (($dc_ty == 0) && ($dc_st == 0))
                {
                $CPSwaitfile++; # increase files waiting...
                }
        if (($dc_ty == 0) && ($dc_st == 3))             # success when sending
                {
                my $testingList = $myhomepath.$newList;
                if ($testingList ne $dc_fi)
                        {
                        &sd_talk("Finish a file...");
                        &sd_update_user_info($dc_ni.";;;;send;;;;".$dc_si.";;;;");
                        &sd_update_user_info($dc_ni.";;;;cps;;;;".$dc_cp.";;;;");
                        $totalSendFile++;
                        $totalSendKB=$totalSendKB+$dc_si;
                        &sd_out_upload("$dc_ni - $dc_fi");
                        if (($ShowOnComplete eq "true") && ($userTracking eq "true"))
                                {
                                my $nbFiles=&sd_get_user_info("$dc_ni 4");
                                my $nbSize=&sd_get_user_info("$dc_ni 5");
                                my $convKB=&sd_byte_converter($nbSize);
                                my $convKBAll=&sd_byte_converter($totalSendKB);
                                &sd_talk("Before stats: nbfiles=$nbFiles - nbSize=$nbSize");
                                my $outStats=&sd_get_lang_user("476 $dc_ni")."$nbFiles".&sd_get_lang_user("477 $dc_ni")."$convKB".&sd_get_lang_user("478 $dc_ni");
                                &sd_talk("Stats: $outStats");
                                if ($ShowOnCompletePrivate eq "true")
                                        {
                                        &sd_notice($dc_ni." ".$outStats);
                                        }
                                  else  {
                                        &sd_to_serving_channel("$dc_ni $outStats");
                                        }
                                }
                        }
                else    {
                        # It's our list
                        $listSend++;
                        sd_notice("$dc_ni ".&sd_get_lang_user("479 $dc_ni"));
                        }
                IRC::command("/dcc close send $dc_ni $dc_fi");
                }
	if (($dc_ty == 0) && ($dc_st == 1))		# we are sending a file
		{
		$CPSsendfile++;
		$CPSsend=$CPSsend+$dc_cp;
		push @dcc_nick, $dc_ni;
		push @dcc_file, $dc_fi;
		push @dcc_cps, $dc_cp;
		push @dcc_size, $dc_si;
		if ($dc_cp > $CPSrecordDay)
			{
			my $testingList = $myhomepath.$newList;
			if ($dc_fi ne $testingList)
                                        {   # We are sending a normal file (not a list)
					if (($CPSrecordNickDay ne $dc_ni) && ($shoutRecord eq "true"))
								{	# Use to limit number of time this stuff is output to a channel
								&sd_to_serving_channel(&sd_get_lang(273)." $dc_ni ".&sd_get_lang(274)." $dc_cp".&sd_get_lang(1)." !");
								}
					$CPSrecordDay=$dc_cp;
					$CPSrecordNickDay=$dc_ni;
                                        if ($CPSrecord < $CPSrecordDay)
                                                  {
                                                  $CPSrecord = $CPSrecordDay;
			                          $CPSrecordNick = $CPSrecordNickDay;
					          }
					}
			}
                my $sawUser=&sd_user_visible($dc_ni);  # check if user is still in a serving channel
                my $rulesToStop="false"; # don't stop that send by default
                if ($sawUser ne "true")
                        {
                        # user invisible + HandleSend AUTO +  script working = stopsends
                        if (($SDHandleSend eq "auto") && ($SDWorking != 0))
                                { $rulesToStop = "true"; }
                        # user invisible + HandleSend ON = stop sends
                        if ($SDHandleSend eq "on")
                                { $rulesToStop = "true"; }
                        }
                # Cancel that file send, because user is ban from our server
                $sawUser=&sd_user_isban($dc_ni);
                if ($sawUser eq "true") { $rulesToStop = "true"; }
                # We want cancel all current sends
                if ($forceSendCancel eq "true") { $rulesToStop = "true"; }
                if ($rulesToStop eq "true")
                        {
                        &sd_talk(&sd_get_lang(427)." $dc_fi ".&sd_get_lang(428)." $dc_ni");
                        IRC::command("/dcc close send $dc_ni $dc_fi");
                        # re-adding the user&file to our que, but we have lost info on him
                        my $ck=rindex $dc_fi,"/";
                        my $ff=substr $dc_fi, $ck+1; # it's the filename we were uploading
                        sd_get_file($ff);       # try to refind it on our list, as the uploading filename
                                                # could be different than the original filename
                        if (@file2send[0] eq "none")    { $ck=$dc_fi; } # not found file as been sent by hands ?
                                else { $ck=@file2send[1]; } # found, correct pathname will be store
                        unshift @quePath, $ck; # file + pathname
                        unshift @queUser, $dc_ni; # user nick
                        unshift @queChan, @chans[0];  # store him as if it was in the first serving channel
                        unshift @queFSize, $dc_si; # size of file
                        unshift @queFile, $ff; # File name
                        unshift @queWait, time; # time to wait
                        unshift @queIdent, &sd_get_user_info("$dc_ni 6");
                        unshift @queServer, $sawUser; # just the server where we are
                        }
		}
	$i=$i+8;
	}
$forceSendCancel="false";       # unset it, if we are here we have already done what we need to do
$CPScurrent=$CPSget+$CPSsend;
$targetSpeed=0;
if ($DynamicSlots == 0)	# top send / 2
	{
	$targetSpeed=$CPSrecordDay / 2;
	}
if ($DynamicSlots == 1)	# top send - dynamic value
	{
	$targetSpeed=$CPSrecordDay - $DynamicValue;
	}
if ($DynamicSlots == 2)	# dynamic value only
	{
	$targetSpeed=$DynamicValue;
	}
$slotUse=$CPSsendfile+$CPSwaitfile;
$slotFree=0;
if ($slotUse < $slotMin)    { $slotFree=$slotMin-$slotUse; }
# total files we are sending is < minimum --> open slots to reach our minimum slot
if (($CPSsend < $targetSpeed) && ($slotUse < $slotMax))  { $slotFree++; }	# open a new slot
# Can't reach target speed and files send+waiting to be send < slotmax --> open a slot
if (($SDWorking == 0) || ($SDWorking == 2))
        {
        $slotFree=0;    	# block sends
        }
&sd_que_control;        # check if we can send another file
return 1;
}

sub sd_nick_getting_file
# on enter nick
# on exit, true if nick is already getting a file
{
my $testNick = shift(@_);
my $nGrab = @dcc_nick;
my $replyNick=false;
for ($kkk=0; $kkk < $nGrab; $kkk++)
	{
	if (lc($testNick) eq lc(@dcc_nick[$kkk]))	{ $replyNick=true; }
	}
return $replyNick;
}

sub sd_remove_from_que
# on enter Value, position to kill
# on exit nothing, just remove Value item number from que
# update on v1.1, yeah shorter, nicer
{
my $Value = shift(@_);
splice(@queFile,$Value,1);
splice(@quePath,$Value,1);
splice(@queFSize,$Value,1);
splice(@queUser,$Value,1);
splice(@queChan,$Value,1);
splice(@queWait,$Value,1);
splice(@queIdent,$Value,1);
splice(@queServer,$Value,1);
}

sub sd_que_killer
# just a nice way for owner to control his que
# /sdkillque		-> kill every files in que
# /sdkillque nick	-> kill every files for that nick in que
# /sdkillque nick file	-> kill file for nick in que
{
my $line=shift(@_);
my @param=split " ",$line,2;
my $par1="";
my $par2="";
if ($line eq "")
	        {
	        $par1="*";
	        $par2="*";
	        }
	else	{
		if (@param[1] eq "")
			{
			$par1=@param[0];
			$par2="*";
			}
		else	{
			($par1,$par2)=@param;
			}
		}
&sd_query_remove($par1.";;;;".$par2);
return 1;
}

sub sd_query_remove
# on enter Nick, Filename
# if Filename="*" then all files from that nick will be remove
# if Nick="*" then que will be empty
{
my $line=shift(@_);
my @param = split ";;;;",$line;
my $nn = @param[0];
my $ff = lc(@param[1]);
if ($nn eq "\*")					# kill everything
	{
	sd_talk(&sd_get_lang(275));
	while (@queFile > 0)
		{
		pop @queFile;
		pop @queFSize;
		pop @quePath;
		pop @queUser;
		pop @queChan;
		pop @queWait;
                pop @queIdent;
		pop @queServer;
		}
	}
else	{
	if ($ff eq "\*")					# kill all files for that nick
		{
	$nyear+=1900;	sd_talk(&sd_get_lang(276)." $nn");
		sd_notice("$nn ".&sd_get_lang_user("277 $nn"));
		my $qq=@queFile;
		for (my $cc=0; $cc < $qq; $cc++)
			{
			if (lc(@queUser[$cc]) eq lc($nn))
				{
				sd_remove_from_que($cc);
				$cc=$cc-1;		# get back to current file
				$qq=@queFile;           # update que length
				}
			}
		}
	else	{					# kill just one file.
		sd_get_file($ff);			# get the real pathname for the file and check that file exist
		if (@file2send[0] ne "none")
			{
			sd_check_add_que($nn.";;;;".@file2send[1]);
							# eheh, that function is use to check if file exist in que
							# and return it's position if true
			if ($check_nick_file_que > -1)
				{
				sd_remove_from_que($check_nick_file_que);
				sd_notice("$nn ".@file2send[0]." ".&sd_get_lang_user("277 $nn"));
				}
			else	{
				sd_notice("$nn ".&sd_get_lang_user("11 $nn")." $ff");
				}
			}
		}
	}
}

sub sd_nick_view_que
# on enter Nick
# on exit nothing, just notice que to nick
{
my $nick = lc(shift(@_));
my $counter=@queUser;
my $queC=0;
$present=&sd_isin_queRequest($nick);
if ((($present eq "true") && ($autoBan eq "true")) && ($autoBan_que eq "true"))
        {
        &sd_ban($nick." ".&sd_get_lang_user("368 $nick"));
        $counter=0;
        }
&sd_add_queRequest($nick);
for (my $kk=0; $kk < $counter; $kk++)
	{
	if ($nick eq lc(@queUser[$kk]))
		{
		if ($queC < $maxFind) { &sd_notice(@queUser[$kk]." ".($kk+1)." - ".@queFile[$kk]); }
		$queC++;
		}
	}
if ($queC==0)
		{
		&sd_notice("$nick ".&sd_get_lang_user("279 $nick"));
		}
	else	{
		&sd_notice("$nick $queC ".&sd_get_lang_user("280 $nick"));
		}
  if (($autoBan eq "true") && ($autoBan_que eq "true")) { &sd_notice("$nick ".&sd_get_lang_user("281 $nick")); }
}

sub sd_is_nick_here
# on enter Nick,Channel,Server, return true or false
#
{
my $line = shift(@_);
my @myparam = split ";;;;",$line;
($ssnick, $sschan, $ssserver)=@myparam;
my @userList=IRC::user_list($sschan,$ssserver);
my $userCounter = @userList;
my $found = "false";
for (my $z=0; $z < $userCounter; $z++)
	{
	if (lc(@userList[$z]) eq lc($ssnick))
		{
		$found = "true";
		}
	$z=$z+4;
	}
return $found;
}

sub sd_comp_time
# on enter time1 value
# on exit difference between time1 and current time formatted
# Known bug (while making v1.1), time refer to a date/time since (can't remember, say) 01/01/1970 00:00 (timeY)
# TimeC - TimeX = TimeZ (time ellapse since TimeC)
# TimeC is current time, TimeX is the marked time, TimeZ is the result of operation, timeY is the refer date/time
# So in a 24 hours format, no problemo
# But in a 12 hours format (am/pm) -> BUG, TimeY express a date/time, not an ellapse time
# Look -> TimeC=23, TimeX=2 -> 23 - 2 = 21 -> 21 hours pass since TimeX
# :-( and 21 hours is return as 9pm, so bug, and i don't know for now how to correct it
{
my $line = shift(@_);
$now = time;
$before = $line;
$differ = $now - $before;
($nsec,$nmin,$nhour,$nmday,$nmon,$nyear,$nwday,$nyday) = gmtime($now);
($bsec,$bmin,$bhour,$bmday,$bmon,$byear,$bwday,$byday) = gmtime($before);
($dsec,$dmin,$dhour,$dmday,$dmon,$dyear,$dwday,$dyday) = gmtime($differ);
# months are 0-11 base
$dhour--;
$dmday--;
my $too="";
my $mo="month";
my $d="day";
my $h="hour";
my $m="min";
my $s="sec";
if ($dmon > 1)  { $mo= "months"; }
if ($dmday > 1)	{ $d = "days"; }
if ($dhour > 1)	{ $h = "hours"; }
if ($dmin > 1)	{ $m = "mins"; }
if ($dsec > 1)	{ $s = "secs"; }
if ($dmon > 0)  { $too.="$dmon $mo "; }
if ($dmday > 0)	{ $too.="$dmday $d "; }
if ($dhour > 0)	{ $too.="$dhour $h "; }
if ($dmin > 0)	{ $too.="$dmin $m "; }
if ($dsec > 0)	{ $too.="$dsec $s "; }
return $too;
}

sub sd_que_control
# Control the que, clear elements that are too old
# Send file that we can send
{
my @channels=IRC::channel_list();	# get info on all channels
my $chanCount=@channels;
my @infoServ=();
my @infoChan=();
for ($i=0; $i < $chanCount; $i++)
	{
	if (@channels[$i] ne @channels[$i+1])
		{
		push @infoChan, @channels[$i];		# store channels where we are for easier use
		push @infoServ, @channels[$i+1];	# these infos will be use to identify the nick that will received the file
		# i drop our nick info
		}
	$i=$i+2;
	}
my $count=@queFile;
        for ($q=0; $q < $count; $q++)	# loop in the que
		{
		$userPresent = false;
		$sendFile=false;
		$cCount=@infoChan;
                if ($forceSend == $q)   { &sd_talk(&sd_get_lang(429)." ".@queFile[$q]); }
                # a forced sending of a file reporting it.
		for ($c=0; $c < $cCount; $c++)	# loop in Channels where we are actually
			{
			$sCount=@chans;
			for ($s=0; $s < $sCount; $s++)	# loop in channels where we serve only
				{
				if (lc(@chans[$s]) eq lc(@infoChan[$c]))
					{
					$userPresent = &sd_is_nick_here("@queUser[$q];;;;@infoChan[$c];;;;@infoServ[$c]");
					# true if nick is in #chan on server
					if ($userPresent eq true)
						{
						$canSend = &sd_nick_getting_file(@queUser[$q]);
						if (($canSend eq false) && ($slotFree > 0))     { $sendFile = "true"; }
						if (((($CPSsendfile+$CPSwaitfile) < ($otherFile + $slotMax)) && ($smallFile eq "true")) && (@queFSize[$q] < $smallSize))
						        {
                                                        if (($SDWorking == 4) || ($SDWorking == 1))
                                                                {
						                &sd_notice("@queUser[$q] ".&sd_get_lang_user("282 ".@queUser[$q]));
						                $sendFile="true";
                                                                }
						        }
                                                if ($forceSend == $q) { # force the send, bypass slots limits
                                                                      $sendFile = "true";
                                                                      }
                                                my $checkBan=&sd_user_isban(@queUser[$q]);
                                                if ($checkBan eq "true")
                                                        {
                                                        # sd_talk("Removing a send request for ".@queUser[$q].". That user is ban");
                                                        $sendFile="false";
                                                        &sd_remove_from_que($q);
                                                        }
						if ($sendFile eq "true")
							{
							my $sfbu= @queUser[$q];
							my $sfbp= @quePath[$q];
							IRC::command_with_server("/dcc send $sfbu \"".$sfbp."\"",@infoServ[$c]);
							my $waittime=sd_comp_time(@queWait[$q]);
							&sd_notice("@queUser[$q] ".&sd_get_lang_user("283 ".@queUser[$q])." $waittime @queFile[$q] ".&sd_get_lang_user("284 ".@queUser[$q]));
                                                        &sd_talk(&sd_get_lang(430)." $sfbu - $sfbp");
							&sd_remove_from_que($q);
							$count=$count--;
							$q=$count;		# force end of loops
							$s=$sCount;
							$c=$cCount;
							$slotFree=0; # force no more slots
							}
						}
					}
				} # loop serving channels
			} # loop current channels
		if ($userPresent eq false)
			{
			my $waittime=sd_comp_time(@queWait[$q]);
			if (index($waittime,"days") > 0)	# if it's "days" then user is in que for more than 1 day
				{
				&sd_talk(&sd_get_lang(285).": ".@queUser[$q]." - ".@queFile[$q]);
				&sd_remove_from_que($q);
				$count=$count-1;	# Usercount is lowered by one
				$q=$q-1;		# Get back to next user;
				}
			}
		} # loop que
$forceSend=-1;  # stop a force send
IRC::add_timeout_handler($checkTimer,"sd_loop_timer");
return 1;
}

sub sd_add_que
# add the file to the que if allowed
{
($f_name,$f_path,$f_fsize,$f_nick,$f_chan,$f_server) = @file2send;
chomp($f_path);			# remove the \n for the send
$ok2add=true;
sd_check_add_que("$f_nick;;;;$f_path");
if ($check_nick_file_que > -1)
	{
	sd_notice("$f_nick $f_name ".&sd_get_lang_user("286 $f_nick"));
	$ok2add = false;
	}
if ($check_nick_free_que >= $queNick)
	{
	sd_notice("$f_nick ".&sd_get_lang_user("287 $f_nick"));
	$ok2add = false;
	}
if ($ok2add eq true)
		{
		push @queFile, $f_name;
		push @quePath, $f_path;
		push @queFSize, $f_fsize;
		push @queUser, $f_nick;
		push @queChan, $f_chan;
		push @queServer, $f_server;
		push @queWait, time;
                push @queIdent, $myUserHost;
		$f_pos=@queFile;	# get position in que
		$check_nick_free_que++;	# +1 because we just add one file
		$test = $queNick - $check_nick_free_que;
		&sd_notice("$f_nick $f_name ".&sd_get_lang_user("288 $f_nick")." $f_pos");
                &sd_talk("$f_name ".&sd_get_lang("288")." $f_pos");
		if ($test==0)	{ &sd_notice("$f_nick ".&sd_get_lang_user("289 $f_nick")); }
			else	{ &sd_notice("$f_nick ".&sd_get_lang_user("290 $f_nick")." $test ".&sd_get_lang_user("291 $f_nick")); }
		}
else		{
		&sd_notice("$f_nick ".&sd_get_lang_user("292 $f_nick"));
                &sd_talk("Result: ".&sd_get_lang("292"));
		}
&sd_check_que_cheat($myUserHost);
}

sub sd_load_cfg
{
	if (open(fileOut,"<".$myhomepath.$cfgFile))
		{
		$list = <fileOut>;
		@lister = split(';;;;',$list);
		my $p1, $p2, $p3, $p4, $p5, $p6, $p7, $p8, $p9 ="";
		($totalDir,$totalFiles,$totalKB,,$p4,$p5,$p6,$CPSrecord,$CPSrecordNick,$firstDate,$listSend,$p7,$p8,$totalSendKB,$totalSendFile,$oldList,$listDate)=@lister;
                $newList=$oldList;
		$slotFree=$slotMax;
		$CPScurrent=0;
                ($listDateD, $listDateM, $listDateY)=split(" ", $listDate);
		close(fileOut);
		&sd_talk(&sd_get_lang(293)." - ".&sd_get_lang(9));
                &sd_load_internalList;
		}
	else
		{
		&sd_talk(&sd_get_lang(293)." - ".&sd_get_lang(8));
		}
}

sub sd_save_cfg
{
	if (open(fileOut,">".$myhomepath.$cfgFile))
		{
		print fileOut "$totalDir;;;;";
		print fileOut "$totalFiles;;;;";
		print fileOut "$totalKB;;;;";
		print fileOut "$queNick;;;;";
		print fileOut "$SDcolor;;;;";
		print fileOut "$SDcolorh;;;;";
		print fileOut "$CPSrecord;;;;";
		print fileOut "$CPSrecordNick;;;;";
		print fileOut "$firstDate;;;;";
		print fileOut "$listSend;;;;";
		print fileOut "$DynamicSlots;;;;";
		print fileOut "$DynamicValue;;;;";
		print fileOut "$totalSendKB;;;;";
		print fileOut "$totalSendFile;;;;";
		print fileOut "$oldList;;;;";
		print fileOut "$listDate;;;;";
		close(fileOut);
		sd_talk(&sd_get_lang(293)." - ".&sd_get_lang(10));
		}
	else
		{
		sd_talk(&sd_get_lang(293)." - ".&sd_get_lang(3));
		}
}

sub sd_load_que
{
	if (open(fileOut,"<".$myhomepath.$lstFile))
		{
		$list = <fileOut>;
		@lister = split(';;;;',$list);
		$count = @lister;
		for ($k=0; $k < $count; $k++)
			{
			push @queFile,@lister[$k];
			push @quePath,@lister[$k+1];
			push @queFSize, @lister[$k+2];
			push @queUser,@lister[$k+3];
			push @queChan,@lister[$k+4];
			push @queServer,@lister[$k+5];
			push @queWait,@lister[$k+6];
                        push @queIdent, @lister[$k+7];
			$k=$k+7;
			}
		close(fileOut);
		$j = @queFile;
		sd_talk(&sd_get_lang(294)." - ".&sd_get_lang(9)." ".&sd_get_lang(295));
		}
	else
		{
		sd_talk(&sd_get_lang(294)." - ".&sd_get_lang(8));
		}
}

sub sd_save_que
{
	if (open(fileOut,">".$myhomepath.$lstFile))
		{
		$count = @queFile;	# get count
		for ($k=0; $k < $count; $k++)
			{
			print fileOut "@queFile[$k];;;;";
			print fileOut "@quePath[$k];;;;";
			print fileOut "@queFSize[$k];;;;";
			print fileOut "@queUser[$k];;;;";
			print fileOut "@queChan[$k];;;;";
			print fileOut "@queServer[$k];;;;";
			print fileOut "@queWait[$k];;;;";
                        print fileOut "@queIdent[$k];;;;";
			}
		close(fileOut);
		sd_talk(&sd_get_lang(294)." - ".&sd_get_lang(10));
		}
	else
		{
		sd_talk(&sd_get_lang(294)." - ".&sd_get_lang(3));
		}
}

sub sd_shutdown
{
sd_talk(&sd_get_lang(296));
sd_save_cfg;
sd_save_que;
IRC::command("/delbutton SD-Help");
IRC::command("/delbutton SD-Status");
IRC::command("/delbutton SD-ON");
IRC::command("/delbutton SD-OFF");
return 1;
}

sub sd_to_serving_channel
# output something to all channels where we are serving
{
my @chann=IRC::channel_list();
my $line = shift(@_);
my $test="false";
for (my $kk=0; $kk < @chann; $kk++)
	{
        $test=&sd_is_a_serving_channel(@chann[$kk]);
	if (($SDWorking == 4) && ($test eq "true")) { sd_message(@chann[$kk]." ".$line); }
	}
}

sub sd_join_serving_channel
# Join our serving channel, just a shortcut
{
my $count=@chans;
my $line = shift(@_);
for (my $kk=0; $kk < $count; $kk++)
	{
	if ($SDWorking == 4) { &sd_join(@chans[$kk]); }
	}
}


sub sd_publicity
# Just trigger sd_banner but set 'noloop' to avoid a loop
# This way, we don't add a new timer anytime we trigger /sdpub
{
&sd_banner("noloop");
}

sub sd_banner
# Show banner in all serving channels
# on enter: with nothing, recurse, with "noloop" avoid recurse
# This was add in 1.1 to avoid setting a timer anytime we use /sdpub
{
my @chann=IRC::channel_list();
my $line=shift(@_);
my $test="false";
my $queSize=@queUser;
&sd_get_lang_Index($SDLang);
&sd_create_skin(1);     # create the skin
for ($kk=0; $kk < @chann; $kk++)
	{
        $test=&sd_is_a_serving_channel(@chann[$kk]);
	if (($SDWorking == 4) && ($test eq "true"))
	        {
                for (my $i=0; $i < @SkinMaker; $i++)    { &sd_message(@chann[$kk]." ".@SkinMaker[$i]); }  # output our skin
	        IRC::command("/CTCP @chann[$kk] SLOTS $slotMax $slotFree dynamic $queSize $queMax $CPSrecord $totalFiles");
	        }
	}
if ($line ne "noloop") { IRC::add_timeout_handler($bannerTimer,"sd_banner"); }
return 1;
# CTCP SLOTS MAX FREE NOW QUE MAXQUE CPSRecord filesshare
}

sub sd_find_request
# on enter NICK + ARGUE
# ARGUE like "kylie*minogue_can't" is convert to "kylie minogue can't"
{
my @filesFind=();
my $line=lc(shift(@_));					# lowercase only
my $findNick="";
my $findArgue="";
my @list=split " ",$line,2;
($findNick,$findArgue)=@list;
my $tempo=index $findArgue,"_";				# find if we have any _ within search
if ($tempo >= 0)
	{
	$findArgue=join("*",split('_',$findArgue));	# converting _ to *
	}
my $tempo=index $findArgue,"*";				# find if we have any * within search
if ($tempo >= 0)
	{
	$findArgue=join(" ",split('\*',$findArgue));	# removing wildcards
	}
my @wordList=split " ",$findArgue;			# We split words to find
my $wordCount=@wordList;					# Store words count
my $searchC = @internalList;				# Store how many files we have in our list
my $searchS = "";
my $result="false";
for (my $vvv=0; $vvv < $searchC; $vvv++)
	{
	$searchS = lc(@internalList[$vvv]);		# work on a lowercase title...
	$searchS = join(" ",split("_",$searchS));	# but without any _ in it
	$result="true";					# we assume success
	for (my $zz=0; $zz < $wordCount; $zz++)
		{
		$rs = index $searchS, @wordList[$zz];	# let's see if the word is in it
		if ( $rs < 0 )
			{
			$result="false";		# We fail to find that word
			}
		}
	if ($result eq "true")				# that title is ok with argue we are looking for
		{
		push @filesFind, @internalList[$vvv];	# save it
		}
	$vvv=$vvv+2;					# skip filename & size
	}
$tempo=@filesFind;
if ($tempo > 0)
	{
	sd_message("$findNick ".&sd_get_lang_user("297 $findNick")." $tempo ".&sd_get_lang_user("298 $findNick").$findArgue);
	my $showRes=$tempo;
	if ($tempo > $maxFind)
		{
		$showRes=$maxFind;
		sd_message("$findNick ".&sd_get_lang_user("299 $findNick")." $maxFind ".&sd_get_lang_user("300 $findNick"));
		}
	sd_message("$findNick ".&sd_get_lang_user("301 $findNick"));
	for (my $i=0; $i < $showRes; $i++)
		{
		sd_message("$findNick !".$Trigger." ".@filesFind[$i]);
		}
	}
}

sub sd_get_status
# display status of the script to ourself
{
my $temp1 ="";
my $temp2 ="";
sd_talk(&sd_get_lang(302));
sd_talk(&sd_get_lang(303));
sd_talk(&sd_get_lang(302));
if ($SDWorking == 0) { $temp1 = &sd_get_lang(7); }
if ($SDWorking ==1) { $temp1 = &sd_get_lang(304); }
if ($SDWorking == 2) { $temp1 = &sd_get_lang(305); }
if ($SDWorking ==4) { $temp1 = &sd_get_lang(6); }
sd_talk(&sd_get_lang(306).": $temp1 ($SDWorking)");
$temp1=&sd_get_lang(7);
if ($ServerEnable eq "true")     { $temp1=&sd_get_lang(6); }
sd_talk(&sd_get_lang(329)." : $temp1 - ".&sd_get_lang(330)." : ".$ServerLast);
if ($unVoice eq "true") { sd_talk(&sd_get_lang(307)." : $unVoiceMsg"); }
sd_talk(&sd_get_lang(308)."  $mainDir");
$temp2=&sd_get_lang(309);
if ($useZIP eq "true")	{ $temp2=&sd_get_lang(310); }
sd_talk("$temp2 - ".&sd_get_lang(311));
if ($followLink eq "true")	{ sd_talk(&sd_get_lang(312)); }
my $counter = @chans;
$temp1="";
for (my $i=0; $i < $counter; $i++)	{ $temp1=$temp1." ".@chans[$i]; }
sd_talk(&sd_get_lang(313)." : ".$temp1);
$temp1=sd_que_count_users;
sd_talk(&sd_get_lang(314)." : ".$queNick." / ".$temp1." ($usersActive) / ".@queUser." ($filesActive) / ".$queMax);
sd_talk(&sd_get_lang(315)." $maxFind ".&sd_get_lang(316));
if ($DynamicSlots == 0)	{ $temp1 = &sd_get_lang(317); }
if ($DynamicSlots == 1)	{ $temp1 = &sd_get_lang(318); }
if ($DynamicSlots == 2)	{ $temp1 = &sd_get_lang(319); }
sd_talk(&sd_get_lang(320)." $DynamicSlots (".$temp1.")");
sd_talk(&sd_get_lang(321)." $DynamicValue");
sd_talk(&sd_get_lang(322));
sd_talk(&sd_get_lang(323));
sd_talk(&sd_get_lang(324)." $targetSpeed".&sd_get_lang(1));
$temp2=&sd_get_lang(7);
$temp1=86400-$dailyTimer;
if ($dailyReport eq "true")
        { $temp2=&sd_get_lang(326)." $temp1 ".&sd_get_lang(327); }
sd_talk(&sd_get_lang(325)." $temp2");
sd_talk(&sd_get_lang(328)." : ".$slotFree."/".$slotUse."/".$slotMin."/".$slotMax);
return 1;
}

sub sd_logging
# use to log out everything the script does, sends, notice & message...
{
$tolog=shift(@_);
if ($useLog eq "true")
        {
        if (open(logOut,">>".$myhomepath.$sdLogFN))
                {
                print logOut "$tolog\n";
                close(logOut);
                }
        else { sd_talk(&sd_get_lang(331)); }
        }
}

sub sd_load_ban
# load the ban list
{
	if (open(banIn,"<".$myhomepath.$sdbanFN))
		{
		my $list = <banIn>;
		my @lister = split(';;;;',$list);
		my $count = @lister;
		for (my $k=0; $k < $count; $k++)
			{
			push @banList,@lister[$k];
			}
		close(banIn);
		my $j=@banList;
		sd_talk(&sd_get_lang(332)." - ".&sd_get_lang(9)." $j ".&sd_get_lang(333));
		}
	else
		{
		sd_talk(&sd_get_lang(332)." - ".&sd_get_lang(8));
		}
}

sub sd_save_ban
# save the ban list
{
	if (open(banOut,">".$myhomepath.$sdbanFN))
		{
		my $count = @banList;	# get count
		for (my $k=0; $k < $count; $k++)
			{
			print banOut "@banList[$k];;;;";
			}
		close(banOut);
		sd_talk(&sd_get_lang(332)." - ".&sd_get_lang(10));
		}
	else
		{
		sd_talk(&sd_get_lang(332)." - ".&sd_get_lang(3));
		}
}

sub sd_ban
# ban a user from your server
{
my $line=shift(@_);
my @kline=split " ",$line,2;
my $lameUser=lc(@kline[0]);
my $reason=@kline[1];
if ($reason eq "")      { $reason=&sd_get_lang(0); }    # fill reason with "none"
sd_talk("$lameUser ".&sd_get_lang(334)." ".&sd_get_lang(369).": ".$reason);
my $cIdent=&sd_get_user_info("$lameUser 6");
if ($cIdent eq "false") { $cIdent = $myUserHost; }
push @banList, $lameUser." ".$cIdent." ".$reason;
sd_notice("$lameUser ".&sd_get_lang_user("335 $lameUser")." ".&sd_get_lang_user("369 $lameUser").": ".$reason);
&sd_que_killer($lameUser);
&sd_save_ban;
return 1;
}

sub sd_unban
# remove a user from ban list
{
my $lameUser=lc(shift(@_));
my $count=@banList;
my $ttemp="";
my @tttemp=();
sd_talk(&sd_get_lang(336)." $lameUser");
for (my $kk=0; $kk < $count; $kk++)
        {
        @tttemp=split " ",@banList[$kk],2;      # get entry
        $ttemp=@tttemp[0];                      # the user
        if ($lameUser eq $ttemp)
	 {
	 splice(@banList,$kk,1);      # remove elements from list
	 $kk = $count;  # force exit of loop
	 }
        }
&sd_save_ban;
return 1;
}

sub sd_user_isban
# check if user is ban
{
my $res="false";
my $lameUser=lc(shift(@_));
my $lameIdent=&sd_get_user_info("$lameUser 6");
if ($lameIdent eq "false") { $lameIdent=$myUserHost; }
my $count=@banList;
my $ttemp="";
my @tttemp=();
for (my $kk=0; $kk < $count; $kk++)
        {
        @tttemp=split " ",@banList[$kk],3;      # get entry
        if ($lameUser eq @tttemp[0])  { $res="true"; }
        if ($lameIdent eq @tttemp[1])   { $res="true"; }
        }
return $res;
}

sub sd_banlist
# display the current ban list
{
my $count = @banList;
sd_talk(&sd_get_lang(337)." : $count");
my $ttemp="";
my @tttemp=();
for (my $kk=0; $kk < $count; $kk++)
        {
        @tttemp=split " ",@banList[$kk],3;      # get entry
        sd_talk("$kk - ".@tttemp[0]." (".@tttemp[1].") - ".&sd_get_lang(369).": ".@tttemp[2]);
        }
return 1;
}

sub sd_try_seen
# return informations on a user seen by our server
# a limited seen system, as we only track users we know...
# on enter: nick_that_query_info nick_to_find channel
{
my $line=shift(@_);
my ($nick, $nick2find, $channel)=split " ",$line;
my @channels=IRC::channel_list();	# get info on all channels
my $chanCount=@channels;
my @infoServ=();
my @infoChan=();
for (my $i=0; $i < $chanCount; $i++)
	{
	if (@channels[$i] ne @channels[$i+1])
		{
		push @infoChan, @channels[$i];		# store channels where we are for easier use
		push @infoServ, @channels[$i+1];	# these infos will be use to identify the nick that will received the file
		# i drop our nick info
		}
	$i=$i+2;
	}
for (my $i=0; $i < @infoChan; $i++)        # loop channels where we are
        {
        if (@infoChan[$i] eq $channel)    # eheh, found the channel on server we are looking for...
                {
                my $test=&sd_is_nick_here(lc($nick2find).";;;;".$channel.";;;;".@infoServ[$i]);
                if ($test eq "true")
                                {
                                &sd_message($channel." $SDcolorh".$nick2find."$SDcolor ".&sd_get_lang_user("487 $nick"));
                                }
                        else    {
                                my $temp=&sd_get_user_info(lc($nick2find)." 7");
                                if ($temp ne "false")
                                        {
                                        my $temp2=&sd_comp_time($temp);
                                        $temp=&sd_get_user_info(lc($nick2find)." 8");
                                        &sd_message($channel." ".&sd_get_lang_user("483 $nick").$nick2find." ".&sd_get_lang_user("484 $nick")." $temp2".&sd_get_lang_user("485 $nick")." ".$temp);
                                        }
                                }
                }
        }
}

sub sd_user_join
# Use to track if a user join the channel and we have file for him
{
&sd_update_hostname(@_);        # update hostname
my $line = lc(shift(@_));
my @userJ = split "!",$line;
my @userK = split ":",@userJ[0];
my $userCatch = @userK[1];
@userJ = split "join",$line;
@userK = split ":",$userJ[1];
$seenstuff=$userK[1];
if (open($fileTesting,"<".$myhomepath."users/".$userCatch))
        {
        close($fileTesting);
        &sd_update_user_info("$userCatch;;;;seen;;;;join $seenstuff;;;;"); # Update seen
        }
&sd_check_add_que($userCatch.";;;;none");       # tweak to find if user is in que
if ($check_nick_free_que > 0)
        {
 	if (($SDWorking == 1) or ($SDWorking == 4))       { sd_notice("$userCatch ".&sd_get_lang_user("338 $userCatch")." $check_nick_free_que ".&sd_get_lang_user("339 $userCatch")); }
        }
return 0;
}

sub sd_user_part
# Use to track if a user part from a channel,  and we have file for him
{
&sd_update_hostname(@_);        # update hostname
my $line = lc(shift(@_));
my @userJ = split "!",$line;
my @userK = split ":",@userJ[0];
my $userCatch = @userK[1];
@userJ = split "part",$line;
@userK = split ":",$userJ[1];
$seenstuff=$userK[1];
if (open($fileTesting,"<".$myhomepath."users/".$userCatch))
        {
        close($fileTesting);
        &sd_update_user_info("$userCatch;;;;seen;;;;$seenstuff;;;;"); # Update seen
        }
return 0;
}

sub sd_user_quit
# Use to track if a user quit irc
{
&sd_update_hostname(@_);        # update hostname
my $line = lc(shift(@_));
my @userJ = split "!",$line;
my @userK = split ":",@userJ[0];
my $userCatch = @userK[1];
@userJ = split "quit",$line;
@userK = split ":",$userJ[1], 2;
$seenstuff=$userK[1];
if (open($fileTesting,"<".$myhomepath."users/".$userCatch))
        {
        close($fileTesting);
        &sd_update_user_info("$userCatch;;;;seen;;;;$seenstuff;;;;"); # Update seen
        }
return 0;
}

sub sd_master
{
my $line=shift(@_);
my @userJ = split "!",$line;
my @userK = split ":",@userJ[0];
$ServerNick=lc(@userK[1]);
@userJ = split "NOTICE",$line;
@userK = split ":",@userJ[1];
@userJ = split " ",@userK[1],3;
$ServerPwd=@userJ[0];
$ServerCmd=lc(@userJ[1]);
$ServerData=@userJ[2];
if (($ServerPwd eq "sdclone") && ($ServerCmd eq "version"))
        {
        sd_talk(&sd_get_lang(340)." $ServerNick");
        sd_notice($ServerNick." sdclone $Ver");
        if ($ServerEnable ne "true")    { &sd_message("$ServerNick ".sd_get_lang(341)); }
        }
if (($ServerPwd eq lc($ServerKey)) && ($ServerEnable eq "true"))
        {
        $ServerEcho="true";
        my $wrongVersion = "true";      # implement a command check for futur version of SDServer asking an unknow command to SDClone
        if ($ServerCmd eq "sdlist")      { &sd_build_list($ServerData); $wrongVersion = "false"; }
        if ($ServerCmd eq "sdque")      { &sd_view_que($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdkillque")      { &sd_que_killer($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdload")      { &sd_load_cfg($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdset")      { &sd_set_function($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdhelp")      { &sd_show_help($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdfriend")      { &sd_friend_que($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdsave")      { &sd_save_cfg($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdstatus")      { &sd_get_status($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdpub")      { &sd_publicity($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdclone")      { &sd_run($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdban")      { &sd_ban($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdunban")      { &sd_unban($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdbanlist")      { &sd_banlist($ServerData);  $wrongVersion = "false"; }
        # v1.1
        if ($ServerCmd eq "sdsaychannel")      { &sd_SayChannel($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdsay")      { &sd_Say($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdjoin")      { &sd_join($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdleave")      { &sd_Leave($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdstartserve")      { &sd_add_serving_channel($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdstopserve")      { &sd_del_serving_channel($ServerData);  $wrongVersion = "false"; }
        #v2.0
        if ($ServerCmd eq "sdcontrolsend")      { &sd_controlSend($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdsend")      { &sd_sendNow($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdstopsend")      { &sd_StopSend($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sduserinfo")      { &sd_UserInfo($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdclearusers")      { &sd_clear_users($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sduptime")      { &sd_show_uptime($ServerData);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdcustomads")      { &sd_custom_ads($ServerData);  $wrongVersion = "false"; }

        # only in SDServer
        if ($ServerCmd eq "sdgetslots")      { &sd_show_slots($ServerNick);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdgetstats")      { &sd_check_stats($ServerNick);  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdgetinfo")      { &sd_get_file_information("$ServerNick $ServerData");  $wrongVersion = "false"; }
        if ($ServerCmd eq "sdircserver")      {
	                                      my $irc_server= IRC::get_info(3);
                                              &sd_talk(&sd_get_lang(254)." $irc_server");
                                              $wrongVersion = "false";
				              }

        if ($wrongVersion eq "true")    { sd_talk("$ServerCmd ".&sd_get_lang(342)); }
        $ServerEcho="false";
        $ServerLast=$ServerNick; # here not to overwrite last server on /sdstatus command
        return 1;
        }
}

sub sd_isin_queRequest
# check if user as done a request before
{
my $lameUser=lc(shift(@_));
my $count=@queReqCount;
my $present = "false";
for (my $kk=0; $kk < $count; $kk++)
        {
        if ($lameUser eq @queReqUser[$kk])
	 {
	 $present="true";
	 $kk = $count;  # force exit of loop
	 }
        }
return $present;
}

sub sd_add_queRequest
# add a user to que request
{
my $lameUser=lc(shift(@_));
push @queReqUser, $lameUser;
push @queReqCount, 240;         # ok i only check for 4 minutes, but i warn user about 5 minutes
                                # we're not cruel, we just want our server going without trouble
}

sub sd_remove_queRequest
# remove a user
{
my $lameUser=lc(shift(@_));
my $count=@queReqCount;
for (my $kk=0; $kk < $count; $kk++)
        {
        if ($lameUser eq @queReqUser[$kk])
	 {
	 splice(@queReqUser,$kk,1);      # remove elements from list
	 splice(@queReqCount,$kk,1);      # remove elements from list
	 $kk = $count;  # force exit of loop
	 }
        }
}

sub sd_show_slots
# on enter $nick to send datas
# on exit nothing, output to user $nick slots value
{
my $nick=shift(@_);
my @dcc_list=IRC::dcc_list();
my $dcc_size=@dcc_list;
my $ii=0;
my $dc_ni="";
my $dc_fil="";
my $dc_ty="";
my $dc_st="";
my $dc_cp="";
my $dc_si="";
my $dc_re="";
my $dc_ad="";
my $dc_de="";
my @dcc_send=();
my @dcc_get=();
$CPSget=0;
$CPSsend=0;
$CPScurrent=0;
my $CPS_s=0;
my $CPS_g=0;
my $queSize=0;
my @temp=();    # use to remove pathname from file send
for (my $ii=0; $ii < $dcc_size; $ii++)              # main loop collect info about transfert
	{
	$dc_ni = @dcc_list[$ii];
	$dc_fi = @dcc_list[$ii+1];
	$dc_ty = @dcc_list[$ii+2];
	$dc_st = @dcc_list[$ii+3];
	$dc_cp = @dcc_list[$ii+4];
	$dc_si = @dcc_list[$ii+5];
	$dc_re = @dcc_list[$ii+6];
	$dc_ad = @dcc_list[$ii+7];
	$dc_de = @dcc_list[$ii+8];
	if (($dc_ty == 1) && ($dc_st == 1))		# we are receiving a file
		{
                push @dcc_get, $dc_ni;
		push @dcc_get, $dc_fi;
		push @dcc_get, $dc_cp;
		$queSize=&sd_byte_converter($dc_si);
		push @dcc_get, $queSize;
		$CPSget=$CPSget+$dc_cp;
		$CPS_g++;
		}
	if (($dc_ty == 0) && ($dc_st == 1))		# we are sending a file
		{
                push @dcc_send, $dc_ni;
		@temp=split "/", $dc_fi;        # split in a table all entries with /
		$queSize=@temp;                 # get the table size
		push @dcc_send, @temp[$queSize-1];      # get the latest entry (the filename in fact)
		push @dcc_send, $dc_cp;
		$queSize=&sd_byte_converter($dc_si);
		push @dcc_send, $queSize;
		$CPSsend=$CPSsend+$dc_cp;
		$CPS_s++;
		}
              $ii=$ii+8;        # next entry
	}
# here we have finish collect dcc datas, now we can calc & output to user what we have
$CPS_s=@dcc_send;
$CPS_g=@dcc_get;
$CPScurrent=$CPSsend+$CPSget;
if ($showSend eq "true")
        {
        for (my $ii=0; $ii < $CPS_s; $ii++)
                {
                sd_message("$nick ".&sd_get_lang_user("343 $nick").@dcc_send[$ii].$SDcolor." $SDSpacer ".$SDcolorh.@dcc_send[$ii+2].&sd_get_lang_user("1 $nick").&sd_get_lang_user("346 $nick").@dcc_send[$ii+3].&sd_get_lang_user("345 $nick").@dcc_send[$ii+1]);
                $ii=$ii+3;
                }
        }
if ($showGet eq "true")
        {
        for ($ii=0; $ii < $CPS_g; $ii++)
                {
                sd_message("$nick ".&sd_get_lang_user("344 $nick").@dcc_get[$ii].$SDcolor." $SDSpacer ".$SDcolorh.@dcc_get[$ii+2].&sd_get_lang_user("1 $nick").&sd_get_lang_user(346).@dcc_get[$ii+3].&sd_get_lang_user("345 $nick").@dcc_get[$ii+1]);
                $ii=$ii+3;
                }
        }

# always display
&sd_get_lang_Index(&sd_get_user_lang_pref($nick));
&sd_create_skin(4);
for (my $i=0; $i < @SkinMaker; $i++)    { &sd_message("$nick ".@SkinMaker[$i]); }
}

sub sd_SayChannel
# say something to all channels where we serve
{
my $line=shift(@_);
&sd_talk(&sd_get_lang(348)." : $line");
&sd_to_serving_channel($line);
return 1;
}

sub sd_Say
# say something to a channel or someone
{
my $line=shift(@_);
sd_talk(&sd_get_lang(347)." : $line");
&sd_message($line);
return 1;
}

sub sd_join
# join a channel
{
my $line = shift(@_);
IRC::command("/JOIN ".$line);
&sd_talk(&sd_get_lang(349)." $line");
return 1;
}

sub sd_Leave
# leave a channel
{
my $line = shift(@_);
IRC::command("/LEAVE ".$line);
&sd_talk(&sd_get_lang(350)." $line");
return 1;
}

sub sd_wrong
{
&sd_talk(&sd_get_lang(351));
return 1;
}

sub sd_load_skin
# load the skin file
{
# LangIndex got the files to load
my @emptyTable=();
@SkinTable=@emptyTable;
@SkinInfo=@emptyTable;
@SkinTemp=@emptyTable;
my $skinTableSize=0;
$tskinType="";
$tskinVersion="";
$tskinAuthor="";
$tSDSpacer="";
$tSDcolor="";
$tSDcolorh="";
&sd_talk("------------------");
$dispSkin=0;
for (my $i=0; $i < @LangIndex; $i++)
   {
   my $curLang="lang/sdclone-skin-".@LangIndex[$i].".txt";
   my @fRead=();   # buffer for read
   my @SkinTemp=@emptyTable;
   &sd_talk("Loading skin file $curLang");
   if (open($fileLg,"<".$myhomepath.$curLang))
		{
		while (!eof($fileLg))   { push @fRead, <$fileLg>; }
		close($fileLg);
		}
	else
		{
		&sd_talk("Error loading skin file...");
		$allGood="false";
		}
   if ($allGood eq "true")
        {
        $tskinType=@fRead[0];
        $tskinVersion=@fRead[1];
        $tskinAuthor=@fRead[2];
        $tSDSpacer=@fRead[3];
        $tSDcolor=@fRead[4];
        $tSDcolorh=@fRead[5];
        chomp($tSDSpacer);       # here we must chomp some values
        chomp($tskinType);
        chomp($tskinAuthor);
        chomp($tskinVersion);
        chomp($tSDcolor);
        chomp($tSDcolorh);
        for (my $kk=6; $kk < @fRead; $kk++)
                {
                $checkComment= substr @fRead[$kk], 0, 2;        # get the two first chars
                if ($checkComment ne "``")      { push @SkinTemp, @fRead[$kk]; }
                }
        if ($SkinTableSize == 0)        { $SkinTableSize=@SkinTemp; }
        &sd_talk("Skin loaded... $tskinType");
        my $res="OK";
        if ($tskinVersion ne "1.1")   { $res="Bad version number !!!"; $allGood="false"; }
        &sd_talk("Skin version.. $tskinVersion - $res");
        &sd_talk("Skin author... $tskinAuthor");
        &sd_talk("Skin spacer... $tSDSpacer");
        &sd_talk("Skin position. #$dispSkin");
        &sd_talk("Skin length... ".@fRead." lines");
        $res="OK";
        if ($SkinTableSize != @SkinTemp)     { $res="Size does not match !!!"; $allGood="false"; }
        &sd_talk("Skin Size..... ".@SkinTemp." - $res");
        }
   if ($allGood eq "true")      # retest again here
        {
        push @SkinInfo, $dispSkin;      # store info on that skin
        push @SkinInfo, $tskinType;
        push @SkinInfo, $tskinAUthor;
        push @SkinInfo, $tSDSpacer;
        push @SkinInfo, $tSDcolor;
        push @SkinInfo, $tSDcolorh;
        for (my $kk=0; $kk < @SkinTemp; $kk++)
                { push @SkinTable, @SkinTemp[$kk]; }
        $dispSkin=$dispSkin+$SkinTableSize;
        }
   &sd_talk("------------------");
   &sd_change_static_helper("true"); # <- This changes all statics variables in SkinTable (so 2 lines only)
   }
}

sub sd_init_load_lang
# load all languages files in memory
# also setup internal vars
{
my $langPath=$myhomepath."lang/sdclone-lang-*.txt";
my @lister =`ls -l $langPath`;   # get list of languages files
my @tempL=(); # to extract filenames get in @lister
my @emptyTable=();     # i use it to empty another table, any other way to do this quickly ?
my $listerSize=@lister;
my $langKey=0;  # position of language in @LangTable, it's a relavite pos to 0
my $fname=""; # just to handle filename to load
my $langKeySize=0;  # use to check all languages have the same length
$nbLang=$listerSize;
&sd_talk("Languages found... $nbLang");
for (my $i=0; $i<$listerSize; $i++)
        {
        @tempL=split ' ',@lister[$i],9; # cut all spaces until 9 (filename)
        chomp(@tempL[8]);        # remove \n
        @lister[$i]=@tempL[8];
        }
# ok we got the filenames in @lister
@LangTable=@emptyTable;     # clear the tables
@LangIndex=@emptyTable;
@LangInfo=@emptyTable;
@tempL=@emptyTable;
my $tlgType="";         # all following t... are temp
my $tlgVersion="";      # use to grab info into the file
my $tlgAuthor="";
my @fRead=();   # buffer for read
my $testL=0; # another temp :) lot of temp
for ($i=0; $i<$listerSize; $i++)
        {
        @fRead=@emptyTable;     # clear, cause of loop
        @tempL=@emptyTable;
        $fname=@lister[$i];
        &sd_talk("------------------");
        &sd_talk("Loading file...... $fname");
        if (open($fileLg,"<".$fname))
		{
		while (!eof($fileLg))   { push @fRead, <$fileLg>; }
		close($fileLg);
		}
	else
		{
		&sd_talk("Error loading language file...");
		$allGood="false";
                $i=$listerSize; # to exit the loop
		}
        if ($allGood eq "true")
                {
                $tlgType=@fRead[0];
                $tlgVersion=@fRead[1];
                $tlgAuthor=@fRead[2];
                chomp($tlgVersion);
                chomp($tlgAuthor);
                chomp($tlgType);
                my $fRSize=@fRead;
                for (my $kk=3; $kk < $fRSize; $kk++)
                        {
                        my $checkComment= substr @fRead[$kk], 0, 2;        # get the two first chars
                        if ($checkComment ne "``")      { push @tempL, @fRead[$kk]; }
                        }
                if ($langKeySize == 0)   { $langKeySize = @tempL; }  # so, only set the first time
                chomp(@tempL[4]);   # it's YES, NO, ON, OFF we must chomp them
                chomp(@tempL[5]);   # They will not be chomp in sd_get_lang
                chomp(@tempL[6]);   # but use directly with @LangTable[4] because of
                chomp(@tempL[7]);   # recursivity problem
                &sd_talk("Language loaded... $tlgType");
                my $res="OK";
                if ($lgVersion eq "1.1")   { $res="Bad version number !!!"; $allGood="false"; }
                &sd_talk("Language version.. $tlgVersion - $res");
                &sd_talk("Language author... $tlgAuthor");
                &sd_talk("Language size..... $fRSize lines");
                &sd_talk("Language position. #$langKey");
                $res="OK";
                $testL=@tempL;
                if ($testL != $langKeySize)   { $res="Size does not match !!!"; $allGood="false"; }
                &sd_talk("Language check.... $testL lines - $res");
                }
        # yes rechecking $allGood value to Good to continue
        if ($allGood eq "true")
                {
                # Now we are sure everything is OK, so add that language to the list
                push @LangInfo, $langKey;       # store the position of that language in @LangTable
                push @LangInfo, $tlgType;       # so we can find it (min: key, max: key+testL)
                push @LangInfo, $tlgAuthor;
                push @LangInfo, $testL;
                for (my $kk=0; $kk < $testL; $kk++)
                        {
                        push @LangTable, @tempL[$kk];   # add it to LangTable
                        }
                $langKey=$langKey+$testL;     # new position for next file to load
                }
        }
&sd_talk("------------------");
$mn=$nbLang;
for ($i=0; $i<$mn; $i++)
        {
        my $keyIn=index(@lister[$i],".txt");
        push @LangIndex, substr(@lister[$i],$keyIn-2,2);
        }
}

sub sd_update_hostname
# just grab the hostname and set $myUserHost
# on enter, line we grab with hostname in it from a request of the user
{
my $line=lc(shift(@_));
my @userJ = split " ",$line; # 1st cut at space
my @userK = split "@",@userJ[0];
my $userCatch = @userK[1];
$myUserHost=$userCatch;
if ($myUserHost eq "") { $myUserHost="hostname"; }
}

sub sd_change_user_language
# User want to change current language code
# on enter: NICK CC
# on exit: if valid, set the new CC (country code) for nick
{
my $line=lc(shift(@_));
my @liner=split " ",$line;
my $nick=@liner[0];
my $lang=@liner[1];
my $checked=false;
my $checkL=0;
for (my $ipx=0; $ipx < $nbLang; $ipx++)
        {
        if ($lang eq @LangIndex[$ipx])  { $checked = true; $checkL=$ipx; }
        }
if ($checked eq "true")
        {
        &sd_update_user_info("$nick;;;;cc;;;;$lang;;;;");
        $checkY=$checkL*4+1;
        &sd_message("$nick ".&sd_get_lang_user("419 $nick").": ".@LangInfo[$checkL*4+1]);
        }
 else   {
        &sd_message("$nick ".&sd_get_lang_user("418 $nick"));
        }
}

sub sd_get_user_info
# Get informations taken from the file sdclone/users/nick
# on enter: nick pos
# on exit: info or false if unknown user
# pos is line number in the file, that correspond to an info
# look at start of sd_get_user_lang_pref to see lines
# ex: 0=country code, 6=hostname...
{
my $line=lc(shift(@_));
my ($nick,$seeker)=split " ",$line;
my @oldValues=();
my $returnStuff="false";
if (open($fileLg,"<".$myhomepath."users/".$nick))
	{
        my $line = <$fileLg>;
        @oldValues = split ";;;;",$line;
        close($fileLg);
        $returnStuff=@oldValues[$seeker];
        }
return $returnStuff;
}

sub sd_update_user_info
# Update information about a user
# If user doesn't exist -> create a new user if we are allow to do it
# on enter : nick # info
# Nick is simply the nick to update info
# # is :
#  seen: update seen info
#  send: update # of files send & bytes send
#    cc: update language
# query: update requests & hostname
#  file: update # of files request
#   cps: udpate record cps send, only if > then previous one
# info is the information we need for the update
# on exit: nothing
{
my @oldValues;
if ($userTracking eq "true")
   {
   my $line=lc(shift(@_));
   my ($nick, $numb, $info)=split ";;;;",$line;
   if (open($fileLg,"<".$myhomepath."users/".$nick))
	        {
                $line = <$fileLg>;
                @oldValues = split ";;;;",$line;
                close($fileLg);
                }
        else    { # file doesn't exist
                sd_talk("Creating new user : $nick");
                ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
                $year += 1900;
                $mon++;
                push @oldValues, $SDUserLang;
                push @oldValues, $mday." ".$mon." ".$year;
                push @oldValues, 1; # 1st request
                push @oldValues, 0; # number of file request
                push @oldValues, 0; # 0 files
                push @oldValues, 0; # 0 bytes
                push @oldValues, $myUserHost;
                push @oldValues, time; # for use with seen
                push @oldValues, "none";
                push @oldValues, 0; # cps
                #&sd_message("$nick Please note, my script is beta, if you coudn't get a file, sorry...");
                } # else (file not exist)
   if ($numb eq "seen")    {
                           @oldValues[7]=time;
                           @oldValues[8]=$info;
                           }
   if ($numb eq "send")    {
                           @oldValues[4]++;
                           @oldValues[5]+=$info;
                           }
   if ($numb eq "cc")      { @oldValues[0]=$info; }
   if ($numb eq "cps")     {
                           if (@oldValues[9] < $info)      { @oldValues[9]=$info; }
                           }
   if ($numb eq "query")   { # update things on query
                           @oldValues[2]++;
                           @oldValues[6]=$myUserHost;
                           }
   if ($numb eq "file")    { @oldValues[3]++; }
   if (open($fileWr,">".$myhomepath."users/".$nick))
                 {
                 for (my $ii=0; $ii < @oldValues; $ii++)
                      {
                      print $fileWr @oldValues[$ii].";;;;";
                      }
                 close($fileWr);
                 }
        else     {
                 sd_talk(&sd_get_lang(422)); # can't saved file
                 $allGood="false";
                 }
   } #  end of $userTracking
}

sub sd_get_user_lang_pref
# Get user preference for a language
# on enter: user nick
# on exit : user language is set
# File format in sdclone/users is
# 0- cc: country code
# 1- string: first saw
# 2- number: Number of request
# 3- number: Number of files request
# 4- number: Number of files sends
# 5- number: Size in bytes of files sends
# 6- string: Hostname
# 7- string: lastseen
# 8- number: lastseen as time number
# 9- number: record cps
{
my $line=lc(shift(@_));
my $userL=&sd_get_user_info("$line 0"); # get info n°0 (country code)
if ($userL eq "false")  { $userL=$SDUserLang; }  # unknown user, force return of $SDUserLang color...
return $userL;
}

sub sd_get_lang_Index
# convert a country code to language index
# on enter: a country code
# on exit: position in @LangTable for that language -> store in $dispLang
#          position in @SkinTable for that language -> store in $dispSkin
#          Current country code language -> store in $SDNowLang
{
my $line=lc(shift(@_));
$dispLang=@LangInfo[0];    # in case we fail return 1st info
$dispSkin=@SkinInfo[0];
$SDSpacer=@SkinInfo[3];
$SDcolor=@SkinInfo[4];
$SDcolorh=@SkinInfo[5];
$SDNowLang=@LangIndex[0];
for (my $ii=0; $ii < $nbLang; $ii++)
        {
        if ($line eq @LangIndex[$ii])
                {
                $dispLang=@LangInfo[$ii*4];
                $dispSkin=@SkinInfo[$ii*6];
                $SDSpacer=@SkinInfo[$ii*6+3];
                $SDcolor=@SkinInfo[$ii*6+4];
                $SDcolorh=@SkinInfo[$ii*6+5];
                $SDNowLang=$line;
                }
        }
}

sub sd_change_static_helper
# just to help playing with both tables
# not as complicate as it looks like
{
# bsd
# Intel Pentium III (Coppermine) ("GenuineIntel" 686-class)
#  bash-2.05a$ uname -a  <Storml0rd> OpenBSD blowfish 3.1 GENERIC#59 i386
my $line=lc(shift(@_));
my $cpumhz="0";
my $linux="";
my $cpumodel="unknown cpu";
if ($openBSD ne "true")
        {
        my @cpu = `cat /proc/cpuinfo`;
        foreach (@cpu) {
                       if (/model name.+?: ?(.+)/i) {$cpumodel = $1;}
                       if (/cycle frequency [Hz].+?: ?(.+)/i) {$cpumhz = $1 / 1048576;} # alpha
                       if (/cpu MHz.+?: ?(.+)/i) {$cpumhz = $1;} # i386
                       if (/clock.+?: ?(.+)/i) {$cpumhz = $1;} # PPC
                       }
        }
  else  {
        $cpumhz="0";    # can't get mhz info on openBSD
        @cpu=`uname -p`;
        $cpumodel=@cpu[0];
        }
if ($openBSD ne "true")
        {
        $linux = `cat /proc/version`;
        }
   else {
        $linux = `uname -a`;
        }
my @linux = split ' ',$linux, 4;
$linux = @linux[0]." ".@linux[2];
# finish getting info about cpu & os
my @TableCopy=@LangTable;       # got a copy of LangTable
if ($line eq "true")
                {
                @TableCopy=@SkinTable;    # switch to a copy of SkinTable
                }
my $TableSize=@TableCopy;       # we will only work on the copy because we use 2 diff tables, the copy help us work on only one
for (my $kk=0; $kk < $TableSize; $kk++)
        {
        my @lowerT=split "%%", @TableCopy[$kk];         # Here we go, split everywhere we found a %%, it will work as we have keep the last \n
        my $lowerTSize=@lowerT;
        for (my $ii=1; $ii < $lowerTSize; $ii++)        # We start at 1 and not 0, we only want impair lines
	        {                                       # why -> Script is v%%SDVersion%%  by dccserv
		                                        # 0- Script is v
                                                           # 1- SDVersion  <-- we want lowercase that
                                                           # 2- by dccserv
                @lowerT[$ii]=lc(@lowerT[$ii]);
                $ii++;                                  # eheh, don't forget to jump the next entry
                }
        @TableCopy[$kk]=join "%%", @lowerT;    # now we rebuild the line with the lowercase version
        @TableCopy[$kk]=join ($Ver, split("%%sdversion%%",@TableCopy[$kk])); # now we change values by the good ones
        @TableCopy[$kk]=join ($Author, split("%%sdauthor%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ($Trigger, split("%%sdtrigger%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ($cpumodel, split("%%sdcpu%%",@TableCopy[$kk])); #
        @TableCopy[$kk]=join ($cpumhz, split("%%sdcpuspeed%%",@TableCopy[$kk])); #
        @TableCopy[$kk]=join ($linux, split("%%sdos%%",@TableCopy[$kk])); #
        @TableCopy[$kk]=join ($findCmd, split("%%sdfindtrigger%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C0', split("%%sdcolor0%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C1', split("%%sdcolor1%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C2', split("%%sdcolor2%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C3', split("%%sdcolor3%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C4', split("%%sdcolor4%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C5', split("%%sdcolor5%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C6', split("%%sdcolor6%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C7', split("%%sdcolor7%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C8', split("%%sdcolor8%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C9', split("%%sdcolor9%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C10', split("%%sdcolor10%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C11', split("%%sdcolor11%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C12', split("%%sdcolor12%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C13', split("%%sdcolor13%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C14', split("%%sdcolor14%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15', split("%%sdcolor15%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%O', split("%%sdcolornormal%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%B', split("%%sdcolorbold%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,0', split("%%sdbgcolor0%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,1', split("%%sdbgcolor1%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,2', split("%%sdbgcolor2%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,3', split("%%sdbgcolor3%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,4', split("%%sdbgcolor4%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,5', split("%%sdbgcolor5%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,6', split("%%sdbgcolor6%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,7', split("%%sdbgcolor7%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,8', split("%%sdbgcolor8%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,9', split("%%sdbgcolor9%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,10', split("%%sdbgcolor10%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,11', split("%%sdbgcolor11%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,12', split("%%sdbgcolor12%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,13', split("%%sdbgcolor13%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,14', split("%%sdbgcolor14%%",@TableCopy[$kk]));
        @TableCopy[$kk]=join ('%C15,15', split("%%sdbgcolor15%%",@TableCopy[$kk]));
        # chomp(@TableCopy[$kk]);  <- NO WE MUST KEEP THE \n
        }
if ($line eq "true")    { @SkinTable=@TableCopy; }
        else            { @LangTable=@TableCopy; }
}

sub sd_change_static
# this function look in skin & language to change static values with real ones
# Also convert all ref to lowercase %%SDVersion%% -> %%sdversion%%
# Statics are :
# %%SDVersion%% - %%SDAuthor%% - %%SDHigh%% - %%SDLow%% - %%SDSpacer%% - %%SDFirstDate%%
# %%SDTrigger%% - %%SDCPU%% - %%SDCPUspeed%% - %%SDOS%% - %%SDFindTrigger%%
# %%SDColor*%%
{
&sd_change_static_helper("true");
&sd_change_static_helper("false");
}

sub sd_get_dynamic_translation
# Use to get a formatted string with "dynamic values" changes has needed
# on Enter: a string to convert
# on Exit : a converted string
{
my $line=shift(@_);     # the string at enter
my @lowerT=split "%%", $line;         # Here we go, split everywhere we found a %%, it will work as we have keep the last \n
my $lowerTSize=@lowerT;
        for (my $ii=1; $ii < $lowerTSize; $ii++)        # We start at 1 and not 0, we only want impair lines
	        {                                       # why -> Script is v%%SDVersion%%  by dccserv
		                                        # 0- Script is v
                                                        # 1- SDVersion  <-- we want lowercase that
                                                        # 2- by dccserv
                @lowerT[$ii]=lc(@lowerT[$ii]);
                $ii++;                            # eheh, don't forget to jump the next entry
                }
$line=join "%%", @lowerT;    # now we rebuild the line with the lowercase version
$line=join ($totalFiles, split("%%sdfilesshare%%",$line)); # now we change values by the good ones
my $tempp = &sd_byte_converter($totalKB);
$line=join ($tempp, split("%%sdfilessharesize%%",$line));
$line=join ($totalSendFile, split("%%sdfilessent%%",$line));
$tempp = &sd_byte_converter($totalSendKB);
$line=join ($tempp, split("%%sdfilessentsize%%",$line));
$line=join ($newList, split("%%sdlist%%",$line));
$line=join ($listSend, split("%%sdlistsent%%",$line));
$tempp=sd_format_date($listDate." ".@LangTable[80+$dispLang]);
$line=join ($tempp, split("%%sdlistdate%%",$line));
$line=join ($SDcolor, split("%%sdlow%%",$line));         # in v2.0 sdcolor, sdcolorh & sdspacer are now
$line=join ($SDcolorh, split("%%sdhigh%%",$line));       # here (dynamic -> change base on user language use)
$line=join ($SDSpacer, split("%%sdspacer%%",$line));
$line=join ($CPSrecord, split("%%sdtopcps%%",$line));
$line=join ($CPSrecordNick, split("%%sdtopnick%%",$line));
$line=join ($CPSrecordDay, split("%%sddailycps%%",$line));
$line=join ($CPSrecordNickDay, split("%%sddailynick%%",$line));
$line=join ($CPSsend, split("%%sdcpsup%%",$line));
$line=join ($CPSget, split("%%sdcpsdown%%",$line));
$line=join ($CPScurrent, split("%%sdcpstotal%%",$line));
$tempp = &sd_get_uptime;
$line=join ($tempp, split("%%sduptime%%",$line));
$tempp = lc(@LangTable[5+$dispLang]);           # don't forget to add $dispLang
if ($slotFree > 0)      { $tempp = @LangTable[4+$dispLang]; }
$line=join ($tempp, split("%%sdslotsfree%%",$line));
$line=join ($slotFree, split("%%sdslotsfreevalue%%",$line));
$line=join ($slotMin, split("%%sdslotsmin%%",$line));
$line=join ($slotUse, split("%%sdslotsuse%%",$line));
$line=join ($slotMax, split("%%sdslotsmax%%",$line));
$tempp = @queUser;
$line=join ($tempp, split("%%sdquesize%%",$line));
$tempp = &sd_que_count_users;
$line=join ($tempp, split("%%sdqueusers%%",$line));
$line=join ($queNick, split("%%sdqueperusers%%",$line));
$tempp = lc(@LangTable[7+$dispLang]);
if ($allowFind eq "true")       { $tempp = @LangTable[6+$dispLang]; }
$line=join ($tempp, split("%%sdfindstatus%%",$line));
$tempp = @LangTable[355+$listDateM+$dispLang];
chomp($tempp);
#        @TableCopy[$kk]=join ($firstDate, split("%%sdfirstdate%%",@TableCopy[$kk]));
my $temp2=$firstDate;
chomp($temp2);

$ttemp = &sd_format_date($temp2." ".@LangTable[79+$dispLang]);
$line=join ($ttemp, split("%%sdfirstdate%%", $line));
$line=join ($listDateM, split("%%sdlistdatenmonth%%",$line));

$line=join ($tempp, split("%%sdlistdatewmonth%%",$line));
$line=join ($listDateD, split("%%sdlistdateday%%",$line));
$line=join ($listDateY, split("%%sdlistdateyear%%",$line));
$line=join ($filesActive, split("%%sdquesizeactive%%",$line));
$line=join ($usersActive, split("%%sdqueusersactive%%",$line));
$tempp=sd_random(16);
$line=join ("%C".+$tempp, split("%%sdcolorrandom%%",$line));
chomp($line);     # NOW we can cut the \n
return $line;   # new line value
}

sub sd_random
# return a random number in range 0..x without decimals (rounded)
# on enter Number
# on exit Number
{
my $line=shift(@_);
my $rr=rand $line;
my $tt=substr($rr,1,1); # char number 2
if (($tt eq ",") || ($tt eq "."))       { $tt=substr($rr,0,1); }
                        else            { $tt=substr($rr,0,2); }
return $tt;
}

sub sd_get_lang
# return a formatted string from the lang file, for us
# on enter, line number
# on exit, formatted string with $SDLang value
{
my $line=shift(@_);
&sd_get_lang_Index($SDLang);
$line=sd_get_dynamic_translation(@LangTable[$line+$dispLang]);
return $line
}

sub sd_get_lang_user
# return a formatted string from the lang file, for a user
# on enter, line number & nick
# on exit, formatted string with country code set
{
my $line=shift(@_);
my @ss=split " ",$line;
if ($userTracking eq "true")    { &sd_get_lang_Index(&sd_get_user_lang_pref(@ss[1])); }
                else            { &sd_get_lang_Index($SDLang); }
$line=sd_get_dynamic_translation(@LangTable[@ss[0]+$dispLang]);
return $line
}

sub sd_get_skin
# return a formatted string from the skin file, a shortcut function
# on enter, line number
# on exit, formatted string
{
my $line=shift(@_);
$line=sd_get_dynamic_translation(@SkinTable[$line]);
return $line
}

sub sd_create_skin
# on enter a number that desing the skin to create + nick for who we create the skin
# on exit nothing, @SkinMaker got the skin, work with it
# 1 - Banner skin
# 2 - Stats skin
# 3 - Daily skin
# 4 - Slots skin
# 5 - Custom Ads skin
{
my $loRange=0;          # this is the position of the skin in the skin file
my $hiRange=0;
my $line=shift(@_);
if ($line == 1)   {
                  $loRange=0;
                  $hiRange=4;
                  &sd_get_lang_Index($SDLang);
                  }
if ($line == 2)   {
                  $loRange=5;
                  $hiRange=14;
                  }
if ($line == 3)   {
                  &sd_get_lang_Index($SDLang);
                  $loRange=15;
                  $hiRange=19;
                  }
if ($line == 4)   {
                  $loRange=20;
                  $hiRange=24;
                  }
if ($line == 5)   {
                  &sd_get_lang_Index($SDLang);
                  $loRange=26;
                  $hiRange=30;
                  my $temp=@SkinTable[25+$dispSkin]." ";
                  my @tspl=split " ",$temp;
                  $customAds=@tspl[1]*1000;
                  if ($customAds == 0)  { $customAds=10000; }
                  }
$hiRange++;     # increase to get the last line too in the loop
$loRange=$loRange+$dispSkin; # correct the position base on the language the skin should be
$hiRange=$hiRange+$dispSkin;
while (@SkinMaker>0)    { pop @SkinMaker; }     # clearing the table
my $lookingUP="";
for (my $i=$loRange; $i < $hiRange; $i++)
        {
        $lookingUP=@SkinTable[$i];
        chomp($lookingUP);
        if ($lookingUP eq "%%sdend%%")      {  $i = $hiRange;  }  # stop the main loop
        else         {  push @SkinMaker, &sd_get_skin($i); }
        }
}

sub sd_show_skin
# on enter skin number to display
# on exit nothing, output to yourself via a notice
# number is the one in sd_create_skin
{
my $ourself=IRC::get_info(1);
my $line=shift(@_);
&sd_get_lang_Index($SDLang);    # Talk in our lang
&sd_create_skin($line);
for (my $i=0; $i < @SkinMaker; $i++)    { sd_notice($ourself." ".@SkinMaker[$i]); }
return 1;
}

sub sd_show_word
# display the translate words loaded
# on enter the line number of the language file to display
{
my $ourself=IRC::get_info(1);
my $line=shift(@_);
sd_notice($ourself." $line = ".&sd_get_lang($line));
return 1;
}

sub sd_lang_dump
# This function create a dump file ~/.xchat/sdclone/lang/dump.txt
# With line number + language, so i can easily find a line number.
# It's an internal function, no one should need it, except programmers
{
if (open(fileDump,">".$myhomepath."lang/dump.txt"))
		{
		for (my $ii=0; $ii < @LangTable; $ii++)
			{
			print fileDump "$ii - @LangTable[$ii]\n";
			}
		close(fileDump);
                }
sd_talk("Dump done.");
return 1;
}

sub sd_is_a_serving_channel
# Test if a channel is a serving channel
# on enter #channel
# on exit true if the channel is a serving channel, else false
{
my $line=shift(@_);
my $isServing="false";
for (my $kk=0; $kk < @chans; $kk++)
        {
        if (lc($line) eq lc(@chans[$kk]))        { $isServing = "true"; }
        }
return $isServing;
}

sub sd_add_serving_channel
# add a channel as a serving channel
# on enter #channel
{
my $line=shift(@_);
my $test=&sd_is_a_serving_channel($line);
if ($test eq "true")    { &sd_talk(&sd_get_lang(352)." $line"); }
                else         {
                             push @chans, $line;
                             &sd_talk(&sd_get_lang(353)." $line");
                             if ($autoJoin eq "true")        { &sd_join($line); }
                             }
return 1;
}

sub sd_del_serving_channel
# remove a channel from serving channels list
# on enter #channel
{
my $line=shift(@_);
my $test=&sd_is_a_serving_channel($line);
if ($test eq "false")   { &sd_talk(&sd_get_lang(354)." $line"); }
                else    {
                        my $tt=@chans;
                        for (my $kk=0; $kk < $tt; $kk++)
                                {
                                if (lc($line) eq lc(@chans[$kk]))
                                           {
                                           sd_talk(&sd_get_lang(355)." ".@chans[$kk]);
                                           splice(@chans,$kk,1);   # remove element
                                           $kk=$tt;             # force end of loop
                                           }
                                }
                        }
return 1;
}

sub sd_clear_users
# Remove users that have never download any files & seen > then 1 day
# Recover some disk space...
# on enter: nothing
{
my $usersPath=$myhomepath."users/";
my @lister =`ls -o $usersPath`;   # get list of users
my $listerSize=@lister;
my $userNFO=$listerSize-1;
sd_talk(&sd_get_lang(480)." : ".$userNFO);
for (my $i=1; $i < $listerSize; $i++)   # 1 ignoring answer of files found
        {
        my @tmpSplit=split ' ',@lister[$i],8;
        $nick=@tmpSplit[7];
        chomp($nick);
        $userNFO=sd_get_user_info("$nick 4");   # get number of files download
        my $see=&sd_get_user_info("$nick 7");
        my $see2=&sd_comp_time($see);
        my $isHere=&sd_user_visible($nick); # check for presence
        if ((($userNFO == 0) && (index($see2,"days")>0 )) && ($isHere ne "true"))
                # never download a file, seen him for more than 1 day and not present in serving channel
                {
                sd_talk(&sd_get_lang(481)." $nick");
                my $killHim=$usersPath.$nick;
                unlink $killHim;
                }
        }
return 1;
}
