#!/usr/bin/perl -w # Diablo Fserve for X-chat irc client. # Copyright (C) 1999 Joseph Elwell # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # to compile: perlcc -o fserve.so fserve.pl #Configure these values. $fserveOn = 1; $fserve_root = "/mnt/win/"; $offering = "Backup Files"; $trigger = "!trigger"; @channels = ("#joesa"); $timer = 300000; # in milli-seconds $maxSends = 1; $max_queues = 50; $max_queues_per_nick = 4; $home_dir = "/home/USER/.xchat/"; $debug = 1; #Do NOT edit anything below if you do not know PERL. #Do NOT edit anything below if you do not know PERL. #Do NOT edit anything below if you do not know PERL. IRC::print("\0035:: Loaded Diablo by Joseph Elwell e-mail bugs to jelwell\@singleclick.com::\003"); IRC::print("\0035:: To Configure Type /fserve::\003"); IRC::register ("Diablo", ".4", "", ""); &initialize(); IRC::add_command_handler("fserve", "fserve_handler"); IRC::add_message_handler("PRIVMSG", "checkTrigger"); IRC::add_message_handler("DCC", "dccMsg"); IRC::add_print_handler("DCC Connected", "connected"); #To find the codes check: /usr/src/redhat/SOURCES/xchat-1.3.12/src/common/text.c IRC::add_print_handler("DCC SEND Complete", "sendNext"); IRC::add_print_handler("DCC SEND Failed", "sendNext"); IRC::add_print_handler("DCC Stall", "sendNext"); IRC::add_print_handler("DCC RECV File Open Error", "sendNext"); IRC::add_print_handler("DCC RECV Failed", "sendNext"); #Each line is a new entry in the array. @msg = ("Download only what you've uploaded, this is meant to be a backup system.","If you have 5Revolution X, or Vib Ribbon email wares\@hotmail.com"); IRC::add_timeout_handler($timer, "timer"); @queueNames = (); @queueFiles = (); %nickqueues = {}; %nickdir = {}; &inputQueues(); sub fserve_handler() { my ($input) = @_; my $userInput = ""; if(substr($input, 0, length("on")) eq "on") { $fserveOn = 1; } elsif(substr($input, 0, length("off")) eq "off") { $fserveOn = 0; } elsif(substr($input, 0, length("root")) eq "root") { $userInput = substr($input, length("root") + 1); $fserve_root = $userInput; } elsif(substr($input, 0, length("trigger")) eq "trigger") { $userInput = substr($input, length("trigger") + 1); $trigger = $userInput; } elsif(substr($input, 0, length("timer")) eq "timer") { $userInput = substr($input, length("timer") + 1); $timer = $userInput; # in milli-seconds } elsif(substr($input, 0, length("maxSends")) eq "maxSends") { $userInput = substr($input, length("maxSends") + 1); $maxSends = $userInput; } elsif(substr($input, 0, length("maxQueues")) eq "maxQueues") { $userInput = substr($input, length("maxQueues") + 1); $max_queues = $userInput; } elsif(substr($input, 0, length("maxQueuesPerNick")) eq "maxQueuesPerNick") { $userInput = substr($input, length("maxQueuesPerNick") + 1); $max_queues_per_nick = $userInput; } elsif(substr($input, 0, length("perl")) eq "perl") { $userInput = substr($input, length("perl") + 1); eval($userInput); } elsif(substr($input, 0, length("offers")) eq "offers") { $userInput = substr($input, length("offers") + 1); $offering = $userInput; } elsif(substr($input, 0, length("channels")) eq "offers") { $userInput = substr($input, length("offers") + 1); push(@channels, $userInput); } elsif(substr($input, 0, length("debug")) eq "debug") { $userInput = substr($input, length("debug") + 1); if ($userInput eq "on") { $debug = 1; } else { $debug = 0; } } else { IRC::print("Command Not understood. Check spelling:\n"); $input = ""; } IRC::print("$input\n"); if ($input eq "") { IRC::print("\0035:: List of valid Commands: ::\003"); IRC::print("\0035:: means type in the value here::\003"); IRC::print("\0035:: For example: /fserve trigger !fserve ::\003"); IRC::print("\0035:: Or: /fserve root /home/jackdoe/fserve/ ::\003"); IRC::print("\0035 /fserve on\003"); IRC::print("\0035 /fserve off\003"); IRC::print("\0035 /fserve root \003"); IRC::print("\0035 /fserve trigger \003"); IRC::print("\0035 /fserve timer \003"); IRC::print("\0035 /fserve maxSends \003"); IRC::print("\0035 /fserve maxQueues \003"); IRC::print("\0035 /fserve maxQueuesPerNick \003"); IRC::print("\0035 /fserve debug on\003"); IRC::print("\0035 /fserve debug off\003"); IRC::print("\0035 /fserve perl \003"); } return 1; } sub shutdown() { &logCommand("DEBUG::Shutting down"); $fserveOn = 0; IRC::command("/dcc closeall"); IRC::print("Diablo Fserve shutdown"); } sub updateTimeout($nick) { my($nick) = @_; my($filename) = $homdir . "/diablo/$nick"; open(FILE, $filename); close(FILE); $now = time; # open # utime $now, $now, $filename; } sub checkTimeout($nick) { my($filename) = $homdir . "/diablo/$nick"; ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($filename); } sub initialize() { &logCommand("DEBUG::Initializing"); if (!-d "~/.xchat") { mkdir("~/.xchat", 0777); } if (!-d "~/.xchat/diablo") { mkdir("~/.xchat/diablo", 0777); } } sub logCommand($cmd) { @cmd = @_; if ($debug == 1) { $logfile = $homedir . "/diablo/logfile.log"; open (LOG, ">>$homedir"); print LOG "@cmd\n"; close(LOG); } } sub inputQueues() { my $i = 0; $logfile = $homedir . "/diablo/queues.log"; open (QUEUES, "<$logfile"); while() { $input = $_; ($nickqueue, $filequeue) = split(/DIABLOFSERVESEPERATOR/, $input); $queueNames[$i] = $nickqueue; $queueFiles[$i] = $filequeue; } close(QUEUES); } sub outputQueues() { $logfile = $homedir . "/diablo/queues.log"; open (QUEUES, "+>$logfile"); for($i=0; $i<$#queueNames; $i++) { my $nickqueue = $queueNames[$i]; my $filequeue = $queueFiles[$i]; my $output = $nickqueue . "/DIABLOFSERVESEPERATOR/" . $filequeue; print QUEUES "$output\n"; } close(QUEUES); } sub sendNext() { &logCommand("DEBUG::Sending File @_"); &outputQueues(); # IRC::print("Sending file"); my $numSends = &listSends("fakeNick", ""); # IRC::print("Number of Files Sending: $numSends"); if ($#queueNames >= 0 && $fserveOn == 1 && $numSends < $maxSends ) { $nick = shift(@queueNames); $file = shift(@queueFiles); $nickqueues{$nick} = $nickqueues{$nick} - 1; $dccCommand = "/dcc send $nick \"" . $file . "\""; IRC::command($dccCommand); } } sub checkSends() { sendNext(); } sub listQueues($nick, $cmd) { my ($nick, $cmd) = @_; &logCommand("DEBUG::Listing Queues"); # $cmd should be "/msg" or "/msg =" for dcc for($i=0; $i<$#queueNames; $i++) { $nickqueue = $queueNames[$i]; $filequeue = $queueFiles[$i]; $cmdtoSend = $cmd . "$nick Slot $i" . "/" . "$max_queues : $nickqueue has queued $filequeue"; IRC::command($cmdtoSend); } if ($i == 0) { IRC::command($cmd . "$nick No Files have been queued."); } } sub clearQueue($nick, $cmd) { my ($nick, $cmd) = @_; my $success = 0; # $cmd should be "/msg " or "/msg =" for dcc for($i=0; $i<$#queueNames; $i++) { $nickqueue = $queueNames[$i]; if ($nickqueue eq $nick) { splice(@nickqueue, $i, 1, splice(@nickqueue, 0, $i)); $cmdtoSend = $cmd . "$nick $nickqueue successfully removed $filequeue from the queue"; IRC::command($cmdtoSend); $success = 1; } } if ($success == 0) { $cmdtoSend = $cmd . "$nick Could not find any files queued by $nick"; IRC::command($cmdtoSend); } } sub delQueue($nick, $file, $cmd) { my ($nick, $file, $cmd) = @_; my $success = 0; # $cmd should be "/msg " or "/msg =" for dcc for($i=0; $i<$#queueNames; $i++) { $nickqueue = $queueNames[$i]; $filequeue = $queueFiles[$i]; if ($nickqueue eq $nick && $filequeue eq $file) { splice(@nickqueue, $i, 1, splice(@nickqueue, 0, $i)); splice(@filequeue, $i, 1, splice(@nickqueue, 0, $i)); $cmdtoSend = $cmd . "$nick $nickqueue successfully removed $filequeue from the queue"; IRC::command($cmdtoSend); $success = 1; } } if ($success == 0) { $cmdtoSend = $cmd . "$nick Could not find $nick with $file in the queue"; IRC::command($cmdtoSend); } } sub addQueue($nick, $file) { my ($nick, $file) = @_; &logCommand("DEBUG::Adding Queue"); if (!fileExists($file)) { IRC::command("/msg =$nick $file does not exist. This fserve is case sensitive"); return 0; } if ($nickqueues{$nick} >= $max_queues_per_nick) { IRC::command("/msg =$nick You've filled all your queue spots"); # &bootNick($nick); return 0; } if ($#queueNames == $max_queues) { IRC::command("/msg =$nick The $#queueNames/$max_queues queues are all full."); return 0; } $nickqueues{$nick} += 1; push(@queueNames, $nick); push(@queueFiles, $file); $counter = $#queueNames + 1; $commandtoSend = "/msg =$nick The file: $file has been placed in request spot " . $counter; IRC::command($commandtoSend); checkSends(); } sub fileExists($file) { my ($file) = @_; &logCommand("DEBUG::FileExists"); # $outtemp = substr($file, 0, length($fserve_root)); # $outtemp .= " != $fserve_root"; if (substr($file, 0, length($fserve_root)) ne $fserve_root || !-e $file) { # IRC::print("NO $outtemp where the new file : $file"); return 0; } else { # IRC::print("YES $outtemp where the new file : $file"); return 1; } } sub listDir($nick) { my ($nick) = @_; &logCommand("DEBUG::ListDir"); if (!$nickdir{$nick}) { $nickdir{$nick} = $fserve_root; } opendir(DIR, "$nickdir{$nick}"); # better end with / while($entry = readdir(DIR)) { $dir = $nickdir{$nick}; $dir .= $entry; if (-d $dir) { $entry = "$entry/" } IRC::command("/msg =$nick $entry"); } closedir(DIR); IRC::command("/msg =$nick [$nickdir{$nick}]"); } sub checkTrigger() { my $line = shift(@_); if ($fserveOn == 1) { $line =~ /:(.*)!(.*@.*) .*:(.*)/; $nick = $1; if ($3 eq $trigger) { if ($nickqueues{$nick} >= $max_queues_per_nick) { &logCommand("DEBUG::Trigger Queues Full"); IRC::command("/msg $nick All your queue spots are filled, you will be allowed back on when you have open queue spots"); } else { &logCommand("DEBUG::Triggered"); # IRC::print "$nick triggered you."; openUpTo($nick); } } } return 0; } sub openUpTo($nick) { my ($nick) = @_; #TODO #checkNickDupe($nick); IRC::command("/dcc chat $nick"); $nickdir{$nick} = $fserve_root; # IRC::print("nickdir is $nickdir{$nick}"); &logCommand("DEBUG::Opening up"); } sub connected() { my $line = shift(@_); #IRC::print "--> $line"; $chat = substr($line, 1, 4); $line =~ s/ /A/c; $line =~ s/ /A/c; $line =~ / /g; $num = pos($line); $nick = substr($line, 6, $num-6); if ($chat eq "CHAT") { foreach $message (@msg) { IRC::command("/msg =$nick $message"); } &listDir($nick); } &logCommand("DEBUG::Connected"); return 0; } sub timer() { &logCommand("DEBUG::Timer called"); IRC::add_timeout_handler($timer, "timer"); $num_queues = $#queueNames + 1; $ad_queues = $num_queues . "/" . $max_queues; $fserve_ad = "2(6File 6Serve 6Online2) 2Trigger:(6" . $trigger . "2) 2Queues:(6" . $ad_queues . "2) Offering:(6" . $offering . "2)"; foreach $channel (@channels) { IRC::command("/msg $channel $fserve_ad"); } } sub getChannels() { return ("#psxunderground"); my @list = IRC::channel_list(); my @online_channels = (); while ($list[0]) { my $chan = shift(@list); my $server = shift(@list); my $nick = shift(@list); push @online_channels, $chan; } # return @online_channels; } sub listSends($nick, $cmd) { my ($nick, $cmd) = @_; &logCommand("DEBUG::Listing Sends"); my @dcc_list = IRC::dcc_list(); my $send_count = 0; my $i=0; # IRC::print("Listing $#dcc_list Sends"); while($i<$#dcc_list) { $dcc_object = { nick => $dcc_list[$i], file => $dcc_list[++$i], type => $dcc_list[++$i], stat => $dcc_list[++$i], cps => $dcc_list[++$i], size => $dcc_list[++$i], resumable => $dcc_list[++$i], addr => $dcc_list[++$i], destfile => $dcc_list[++$i], }; ++$i; # IRC::print("nick: $dcc_object->{type}"); # IRC::print("file: $dcc_object->{file}"); # IRC::print("type: $dcc_object->{type}"); # IRC::print("stat: $dcc_object->{stat}"); # IRC::print("cps: $dcc_object->{cps}"); # IRC::print("size: $dcc_object->{size}"); # IRC::print("resumable: $dcc_object->{resumable}"); # IRC::print("addr: $dcc_object->{addr}"); # IRC::print("destfile: $dcc_object->{destfile}"); if ($dcc_object->{type} == 0 && $dcc_object->{stat} == 1) { my $cmdtoSend = $cmd . "$nick Sending $dcc_object->{nick} $dcc_object->{file} at $dcc_object->{cps} cps"; IRC::command($cmdtoSend) if ($cmd ne ""); $send_count++; } elsif ($dcc_object->{type} == 0 && $dcc_object->{stat} == 2) { # $send_count++; } } if ($send_count == 0) { IRC::command($cmd . "$nick No Files are being sent.") if ($cmd ne ""); } return $send_count; } sub dccMsg() { my $line = shift(@_); &logCommand($line); $line =~ s/ /A/c; $line =~ / /g; $num = pos($line); $shortenedline = substr($line, $num); $shortenedline =~ /:/g; $num2 = pos($shortenedline); $nick = substr($shortenedline, 0, $num2-1); $command = substr($shortenedline, $num2+1); if (($command eq "dir") || ($command eq "ls")) { &listDir($nick); } elsif (substr($command, 0, 2) eq "cd") { #Protect from cd'ing below fserve root... @dirs = split(/\//, $nickdir{$nick}); if (substr($command, 3, 2) eq "..") { pop(@dirs); # $dirs[$#dirs] = ""; $newdir = join('/', @dirs); $newdir .= "/"; &changeDir($nick, $newdir); } else { $newdir = substr($command, 3); $newdir =~ s/[;`'\/><{}]*//g; # $newdir =~ s/`//g; # $newdir =~ s/'//g; # $newdir =~ s/\///g; # $newdir =~ s/>//g; # $newdir =~ s/