#!"g:\cgi\perl\bin\perl.exe" -w
####################################
# @Author: Oopos
# 2009-01-17 05:01
# Just for fun,free for all.
####################################
print "Content-type:text/html;charset=gb2312\n\n";

##########################
# setting config
##########################
my %OPSCNF = ();
$OPSCNF{'MAX_FILE_LEN'} = 128 ;
$OPSCNF{'YES_FILE_TYPE'} = "";
$OPSCNF{'NO_FILE_TYPE'} = "php|exe";
$OPSCNF{'MAX_UPLOAD_SIZE'} = 1024*1024*1024 ; # 1024M
$OPSCNF{'CGI_UPLOAD_HOOK'} = 1 ;
$OPSCNF{'SLEEP_TIME'} = 0 ;
$OPSCNF{'TMP_DIR'}	=	'G:/webserver/xampp/cgi-bin/ops_perl/tmpdir';
$OPSCNF{'UPLOAD_DIR'}	=	'G:/webserver/xampp/cgi-bin/ops_perl/upload';
$OPSCNF{'UPKEY_DIR'} = 'C:/PhpServer/www/multiupload/ops_tmp/key';
$OPSCNF{'UPKEY_LEN'} = 16;
$OPSCNF{'FILE_FIELD'} = 'upfile';
$OPSCNF{'EACH_BYTES'} = 1024*256 ; # 256KB each time
$OPSCNF{'SPEED_LIMIT'} = 1;
$OPSCNF{'NO_SAME_FILES'} = 0 ; # hook the same files
$OPSCNF{'DO_DEBUG'}	=	0;
$OPSCNF{'GOOD_SPEED'} = 1024*1024 ;#$OPSCNF{'EACH_BYTES'};
$OPSCNF{'UPDATE_PHP_PATH'} = 'C:/PhpServer/www/multiupload/update.php';
$OPSCNF{'MUST_DO_DISK'} = '1_2' ; # like this 1_2_3
##########################
# progress msg info
##########################
#{"filekey":"","error":"","total":121838,"current":121838,"speed":23369717.4167,
#"filename":"lang1.jpg","name":"file0","temp_filename":"L:\\uploadtmp\\php1E.tmp",
#"cancel_upload":0,"done":1,"start_time":1231307339.27,"end_time":}
my %OPSINFO = ();
$OPSINFO{'precent'} = 0 ; #$total == 0 ? 'Unknow' : intval(100*($current / $total));
$OPSINFO{'speed'} = 'Unknow' ; #size_format($speed,'s');
$OPSINFO{'speedbar'} = 0 ; #intval($speed/$goodspeed*100); // 256KB is good rate
$OPSINFO{'total'} = 'Unknow' ; #$total;
$OPSINFO{'size'} = 'Unknow'; #size_format($total);
$OPSINFO{'nowsize'} = 'Unknow' ; size_format($current);
$OPSINFO{'starttime'} = 'Unknow' ; #date('Y-m-d H:i:s',$start_time);
$OPSINFO{'donetime'} = 'Unknow';  #date('Y-m-d H:i:s',time());
$OPSINFO{'remaintime'} = 'Unknow' ; #$speed == 0 ? 'Unknow' : sprintf("%0.2f",($total-$current)/$speed);
$OPSINFO{'done'} = 0; #$done;
$OPSINFO{'cancel'} = 0; #$cancel_upload;
$OPSINFO{'costtime'} = 'Unknow'; #sprintf("%0.2f",$cost_time);
$OPSINFO{'error'} = 0;
$OPSINFO{'name'} = 'Unknow';
$OPSINFO{'type'} = 'Unknow';
$OPSINFO{'filekey'} = 'Unknow';
$OPSINFO{'saving'} = 0;

##########################
# global begin
##########################
$|	=	1;
use strict;
use CGI::Carp 'fatalsToBrowser';
use CGI qw(:cgi);
use File::Copy;
use File::Path;
use FileHandle;
use Time::HiRes qw( gettimeofday tv_interval );

# do safe operation
$ENV{'PATH'} = 'g:/cgi/perl/bin';
delete @ENV{'IFS','CDPATH','ENV','BASH_ENV'};


######################### Begin main operation ##############################
# get query string
my %query_string = parse_query_string($ENV{'QUERY_STRING'});
# check the upload file key
$OPSINFO{'filekey'} = $query_string{'upkey'};
if( check_upkey() != 1 ){
	gotoexit("error","Illegal upload key!");
}
umask(0);
# set max post size 
$CGI::POST_MAX = $OPSCNF{'MAX_UPLOAD_SIZE'};
# mkdir path 
if (!(-e $OPSCNF{'UPLOAD_DIR'}) ){
	mkpath($OPSCNF{'UPLOAD_DIR'},0,0777) or gotoexit("mkdir_error","Cannot make path !");
}
if ( !(-e $OPSCNF{'TMP_DIR'}) ){
	mkpath($OPSCNF{'TMP_DIR'},0,0777) or gotoexit("mkdir_error","Cannot make path !");
}
if( $ENV{'CONTENT_LENGTH'}  <= 0 ){
	gotoexit("file_toosmall","Your file is less than a byte.");
}
if( $ENV{'CONTENT_LENGTH'} > $OPSCNF{'MAX_UPLOAD_SIZE'} ){
	gotoexit("file_toobig","Your file is too large.");
}
elsif($OPSCNF{'CGI_UPLOAD_HOOK'} && $CGI::VERSION <= 3.15){
	gotoexit("server_error","CGI.pm ver" . $CGI::VERSION . " does not support an upload hook");
}else{
	#print %ENV ;
}

if ( $OPSCNF{'SLEEP_TIME'} > 0 ){
	print "<br>start to sleep...<br>";
	sleep($OPSCNF{'SLEEP_TIME'});
}

my $ops_limit = 1;
ops_upload_file(0);

######################### End main operation ##############################


##########################
# to upload files
##########################
sub ops_upload_file{
	my $ops_count = shift;
	if($ops_count eq "") { $ops_count = 0 ; }
	my $ops_cgi = new CGI;
	my $ops_upfile = $ops_cgi->param($OPSCNF{'FILE_FIELD'} . $ops_count);
	my $upid = '';
	my @disks = ("null","rayfile","namipan","91files","zippyshare","rapidshare","easyshare");
	for(my $i=1; $i < @disks; $i ++){
	if( $ops_cgi->param($disks[$i]) == $i ){
		if($upid == '' ){ $upid = $i; }
		else{ $upid .= '_'.$i; }
	  }
	}
	if($upid eq "" ){
		if( $OPSCNF{'MUST_DO_DISK'} eq "" ){
		gotoexit("no_disk_seleced","No disks to upload!");
		}else{
		$upid = $OPSCNF{'MUST_DO_DISK'};
		}
	}
	if ($ops_upfile ne "") {
		#print "file" . $ops_count . " is:" . $ops_upfile . "<br>";
		my $ops_filename = $ops_upfile ;
		$ops_filename =~ s/^.*(\\|\/)//;
		####### check files and return the fileinfo ########
		my %fileinfo = check_files($ops_filename);
		#print "<br>len:".$fileinfo{"len"}." name:".$fileinfo{"name"}." type: ". $fileinfo{"type"} ." <br>";
		if ($fileinfo{'error'} ne ""){
			return $fileinfo{'error'}
		}
		my $safe_filename = $fileinfo{'name'}.'.'.$fileinfo{'type'} ;
		################## begin to read data #########################
		my $outfile = $OPSCNF{"TMP_DIR"}."/".$safe_filename ;
		if( -e $outfile ){
			# do rename 
			my $tmp_name = re_filename($OPSCNF{'TMP_DIR'},$safe_filename,100);
			if($tmp_name eq ""){
				gotoexit("tmp_rename_error","Rename file so much times and still failed,Maybe your RP is so low.");
			}
			$safe_filename = $tmp_name;
			$outfile = $OPSCNF{"TMP_DIR"}."/".$safe_filename ;
		}
		$OPSCNF{'OUT_FILE'} = $outfile;
		open(OUTFILE,">$outfile");
		binmode(OUTFILE);
		$OPSINFO{'name'} = $ops_filename;
		$OPSINFO{'type'} = $fileinfo{'type'};
		$OPSINFO{'total'} =  $ENV{'CONTENT_LENGTH'};
		$OPSINFO{'error'} = 0 ;
		$OPSINFO{'size'} = size_format($OPSINFO{'total'});
		$OPSINFO{'nowsize'} = size_format($OPSINFO{'current'});
		$OPSINFO{'starttime'} = date();
		$OPSINFO{'costtime'} = 0.00 ;
		my $total = $OPSINFO{'total'};
		my $start = gettime();
		my $now = gettime();
		my $current = 0;
		my $speed=0;
		my $bytes;
		my $tmpbuf;
		while ( $bytes = read($ops_upfile,$tmpbuf,$OPSCNF{"EACH_BYTES"}) ){
			print OUTFILE $tmpbuf ;
			$now = gettime();
			$OPSINFO{'costtime'} = sprintf("%0.2f",$now-$start);
			$current += length($tmpbuf);
			$speed = sprintf("%0.2f",$current/($now-$start));
			$OPSINFO{'speed'} = size_format($speed).'/s';
			$OPSINFO{'speedbar'} = sprintf("%0.1f",$speed/$OPSCNF{'GOOD_SPEED'});
			$OPSINFO{'current'} = $current;
			$OPSINFO{'nowsize'} = size_format($OPSINFO{'current'});
			$OPSINFO{'remaintime'} = sprintf("%0.2f",($total-$current)/$speed) ;
			$OPSINFO{'precent'} = sprintf("%0.1f",100*($current/$total));
			update_progress();
			if($OPSCNF{'SPEED_LIMIT'} == 1){ 	sleep(1); }
			
		}
		close(OUTFILE);
		# begin to saving
		$OPSINFO{'saving'} = 1;
		$OPSINFO{'total'} = $total = $current ;
		$OPSINFO{'size'} = size_format($total);
		$OPSINFO{'precent'} = 100 ;
		################## end to read data  #########################	
		
		my $destfile = '';
		################## begin save the dest data ##################
		if ( -e $outfile ){
			#print "<br>Upload Success!<br>";
			#print "But you should only wait a while to wait the server save your files,wait...<br>";
			$destfile = $OPSCNF{'UPLOAD_DIR'}.'/'.$safe_filename;
			if( -e $destfile ){
				if($OPSCNF{'MAX_UPLOAD_SIZE'} != 0 && (-s $outfile) > $OPSCNF{'MAX_UPLOAD_SIZE'} ){
					gotoexit("file_toobig","You cannot upload so big files.");
				}
				if($OPSCNF{'NO_SAME_FILES'} == 1 && (-s $destfile) == (-s $outfile) ){
					gotoexit("no_samefile","You cannot upload the same files.");
				}
				# do rename 
				my $tmp_name = re_filename($OPSCNF{'UPLOAD_DIR'},$safe_filename,100);
				if($tmp_name eq ""){
					gotoexit("dest_rename_error","Rename file so much times and still failed,Maybe your RP is so low.");
				}
				$safe_filename = $tmp_name;
				$destfile = $OPSCNF{"UPLOAD_DIR"}."/".$safe_filename ;
			}
			
			$OPSCNF{'DEST_FILE'} = $destfile;
			
			move($outfile,$destfile) or copy($outfile,$destfile) or gotoexit("save_file_error","Cannot save file to dest dir!");
			chmod 0666, $destfile;
			
			unlink($outfile);
			
			#print "<br>Save file successful,too.<br>";
			#print "<br>You Can close the brower.<br>";
			
			$OPSINFO{'saving'} = 2 ;
		}
		else{
			#print "<br>Upload Failed!<br>";
			$OPSINFO{'saving'} = 3 ;
		}
		$OPSINFO{'donetime'} = date();
		$OPSINFO{'done'} = 1;
		update_progress();
		
		if( $OPSINFO{'saving'} == 2 ){
			# upid=&name=&type=&size=&save&ops=ops520zhxdong520vcacm520oopos1988oopos11zhxdong30
			my $name = $fileinfo{'name'};
			my $type = $fileinfo{'type'};
			my $size = $total ;
			my $save = $destfile;
			my $opsapi = 'ops520zhxdong520vcacm520oopos1988oopos11zhxdong30';
			my $querystr = "upid=$upid&name=$name&type=$type&size=$size&save=$save&ops=$opsapi";
			
			#$cmdstr =~ s/&/\!/g;
			#print "<br>cmdstr:".$cmdstr."<br>";
			#my $redata = exec($cmdstr);
			
			########## HTTP GET ###################
			use LWP::UserAgent; 
			use HTTP::Request; 
			use HTTP::Response; 
			my $url = 'http://' . $ENV{'HTTP_HOST'} . '/update.php?' . $querystr ;
			#printf "%s =>\n\t<br>", $url;
			my $usa = LWP::UserAgent->new(); 
			my $req = HTTP::Request->new(GET => $url); 
			my $response = $usa->request($req);
			if ($response->is_error()) {
			     print '{"error":"'.$response->status_line.'"}';
			 } else {
			     my $content = $response->content();
			     print $content;
			 }
			#######################################
			
			$OPSINFO{'saving'} = 4 ;
			update_progress();
			
			#my $upcmdstr = "start php -a ".$OPSCNF{'OPS_UPLOAD_PHP'}." 
			
			#exec("start php -f C:/PhpServer/www/multiupload/opsuper.php 126 1_2 G:/webserver/xampp/cgi-bin/ops_perl/upload/y0m8xdfko_linux.txt");
			
			#print "<br>===========================<br>";
			#print "Cmd data:".$redata."<br>";
		
		}
		
		#print "Total use time:".sprintf("%0.6lf",$now-$start)." s<br>";
		################## end  save the dest data ##################
	}else{
		gotoexit("nofile_upload","Error: No file selected!");
	}
}

##########################
# get time of sec usec
##########################
sub gettime{
	my ($sec, $usec) = gettimeofday;
	return sprintf("%0.6lf",$sec+($usec/1000000));
}

##########################
# update the progress files
##########################
sub update_progress{
	if( $OPSINFO{'filekey'} eq "" ){
		return 0;
	}
	my @keya = keys(%OPSINFO);
	my @vala = values(%OPSINFO);
	my $tmp_len = @keya;
	my $emsg = '{';
	for(my $i=0; $i < $tmp_len; $i ++){
		$emsg .= '"'.$keya[$i].'":"'.$vala[$i].'",';
	}
	$emsg .= '"author":"oopos"}';
	write_files($OPSCNF{'UPKEY_DIR'}.'/'.$OPSINFO{'filekey'}.'.upkey',$emsg);
}

##########################
# date:2009-11-12 08:08:08
##########################
sub date{
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
 return sprintf("%4d-%02d-%02d %02d:%02d:%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec);
}

##########################
# go end and show error
##########################
sub gotoexit{
	my $errno = shift;
	my $error = shift;
	my $emsg = "Error [$errno]: $error";
	my $jsonmsg = '{"error":"'.$error.'"}';
	# create error files 
	write_files($OPSCNF{'TMP_DIR'}.'/'.$OPSINFO{'filekey'}.'.error',$emsg);
	if($OPSCNF{'OUT_FILE'} ne ""){
		unlink($OPSCNF{'OUT_FILE'});
	}
	die($jsonmsg);
}

##########################
# write something to file
##########################
sub write_files{
	my $tmp_file = shift;
	my $tmp_text = shift;
	my $tmp_handle = new FileHandle;
	$tmp_handle->open("> $tmp_file") or die("Failed to create or open $tmp_file");
	$tmp_handle->write($tmp_text);
	chmod 0777, $tmp_file;
	$tmp_handle->close();
}

##########################
# to check file key
##########################
sub check_upkey{
	
	my $ops_upkey = $query_string{'upkey'};
	if($ops_upkey eq ""){
		return -3;
	}
	if( length($ops_upkey) != $OPSCNF{'UPKEY_LEN'} ){
		return -2;
	}
	if( $ops_upkey =~ m/[^a-z0-9]/g ){
		return -1;
	}
	my $ops_upkey_file = $OPSCNF{'UPKEY_DIR'}."/".$ops_upkey.".upkey";
	if(-e $ops_upkey_file){
		return 1;
	}
	return 0;
}

##########################
# check files
##########################
sub check_files{
	my $ops_filename = shift;
	$ops_filename =~ s/[^\x80-\xff\w\.]//isg ; # make the name safe
	my %ops_info = parse_filename($ops_filename);
	
	if($ops_info{'error'} ne "" ){
		$ops_info{'error'} = "Illegal File!";
	}
	elsif($OPSCNF{'YES_FILE_TYPE'} ne ""){
		my @yesa = split(/\|/,$OPSCNF{'YES_FILE_TYPE'});
		if( !in_array($ops_info{'type'},@yesa) ){
			$ops_info{'error'} = "Wrong file types!";
		}
	}
	elsif($OPSCNF{'NO_FILE_TYPE'} ne ""){
		my @noa = split(/\|/,$OPSCNF{'NO_FILE_TYPE'});
		if( in_array($ops_info{'type'},@noa) ){
			$ops_info{'error'} = "Wrong file types!";
		}
	}
	if( $ops_info{'len'} > 200 || $ops_info{'len'} > $OPSCNF{'MAX_FILE_LEN'} ){
		$ops_info{'name'} = substr($ops_info{'name'},$ops_info{'len'}-$OPSCNF{'MAX_FILE_LEN'});
	}
	return %ops_info;
}

##########################
# parse filename
##########################
sub parse_filename{
	my $ops_filename = shift;
	my $pdot = rindex($ops_filename,".");
	my %info = ();
	$info{'len'} = length($ops_filename);
	if( $pdot <= 0 || $pdot == $info{'len'}-1 ){
		$info{'error'} = 'Wrong file name.';
		$info{'name'} = '';
		$info{'type'} = '';
		return %info;
	}
	$info{'error'} = "";
	$info{'name'} = lc(substr($ops_filename,0,$pdot));
	$info{'type'} = lc(substr($ops_filename,1-($info{'len'}-$pdot)));
	return %info ;
}

##########################
# size format
##########################
sub size_format{
	my $bytes = shift;
	if( $bytes eq "" || $bytes == 0 ){
		return "0 B";
	}
	my @sarr = ("B","KB","MB","GB","TB","PB","EB","ZB","YB");
	my $e = int(log($bytes)/log(1024));
	my $pe = sprintf ("%0.2f",$bytes / (1024 ** $e) ); 
	my $res =  $pe." ".$sarr[$e] ;
	return $res;
}

##########################
#	if e in array?
##########################
sub in_array{
	my $pt = shift;
	my @arr = shift;
	foreach my $val (@arr) {
		if( $pt eq $val ){	return 1;}
	}
	return 0;
}

#########################
#	strip the whitespace
#########################
sub trim{
	my $string = shift;
	$string =~ s/^\s+//; # Trim left
	$string =~ s/\s+$//; # Trim right
	return $string;
}

#########################
#	get random string
#########################
sub get_random_string{
	my $length_of_randomstring = shift;
	my @chars=('a'..'z', '0'..'9');
	my $random_string="";
	for(my $i = 0; $i < $length_of_randomstring; $i++){ $random_string .= $chars[int(rand(36))]; }
	return $random_string;
}

##########################
#	get filesize
##########################
sub get_filesize{
	my $upload_dir = shift;
	my $file_name = shift;
	my $path_to_file = $upload_dir .'/'. $file_name;
	my $file_size = 0;

	if(-e $path_to_file && -f $path_to_file){ $file_size = -s $path_to_file; }

	return $file_size;
}
##########################
#	Parse query string
##########################
sub parse_query_string{
	my $buffer = shift;
	my @pairs = split(/&/, $buffer);
	my %query_string = ();
	foreach my $pair (@pairs){
		my ($name, $value) = split(/=/, $pair);
		$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
		$query_string{$name} = $value;
	}
	return %query_string;
}

##########################
#	Rename  same files
##########################
sub re_filename{
 	my $path = shift;
 	my $name = shift;
 	my $limit = shift;
 	if($limit eq ""){
 		$limit = 100;
 	}
 	my $tname = $name ;
 	while( (-e $path.'/'.$tname) && $limit > 0){
 		$limit --;
 		#print "<br>Times: $limit begin rename ...<br>";
 		$tname = get_random_string(9).'_'.$name;
 	}
 	if( -e $path.'/'.$tname ){ return "";}
 	return $tname;
}