#!/usr/bin/perl -w

eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
    if 0; # not running under some shell
#########################################################################
#
# Usage: smestorage mountpoint user:password [providerUser:providerPassword] [--ao] [--DEBUG]
# Prereq: Fuse
# 
#########################################################################

use strict;
use warnings;

#core
use POSIX qw(:errno_h :fcntl_h);
use File::Spec::Functions;

#preregs
use Fuse;
#use Term::ReadKey;

use Data::Dumper;
use XML::Simple;
use HTML::Parser;
use LWP::UserAgent;
use MIME::Base64;

use Storable qw(store_fd fd_retrieve);
use IO::Pipe;
#use IO::Handle;
use IO::Select;
use IO::Socket;

use Time::Local;

my $VERSION="3.0.6";
my $server_host='';
my $server_sme='smestorage.com';
my $server_sme_api='http://'.$server_sme.'/api/';
my $token = '*';

my @filelist;
my $startSignIn=0;

#binmode STDOUT, ':utf8';

use constant DEBUG => 0;
my $DEBUG=0;
my $SMALL_FILE_SIZE=1048576;			# 1048576 - 1Mb. If file size less than $SMALL_FILE_SIZE, then rapid upload is used

#initial stuff
my $homedir = catdir($ENV{HOME},'.smestorage');
mkdir $homedir unless -d $homedir;

my $mountpoint;
my $smelogin;
my $smepassword;
my $providerUser="";
my $providerPassword="";

my $allow_other=0;
my $printToLogFile=0;
my $needSync=0;
my $lastRequestToServer=0;

loadConfig();

#get command line arguments
my $i=0;
while(defined $ARGV[$i]){
	if(index($ARGV[$i], "--DEBUG")!=-1){
    $DEBUG=1;
		my $j0=0;
		while($j0<20){
			if(index($ARGV[$i], "--DEBUG" . $j0)!=-1){
	      $DEBUG=$j0;
			}
			$j0++;
		}
	}

	$allow_other=1 if($ARGV[$i] eq "--ao");
	$printToLogFile=1 if($ARGV[$i] eq "--al");
	$needSync=1 if($ARGV[$i] eq "--sync");
	if($ARGV[$i]=~/^--server=(.+)/i){
		$server_host=$1;
		splice(@ARGV, $i, 1);
		$i--;
	}

	$i++;
}


if(defined $server_host && $server_host ne ""){
	$server_host=$1 if($server_host=~/^http[s]{0,1}:\/\/([A-z\.]+)[\/]{0,1}/i);
	if(defined $server_host && $server_host ne ""){
		$server_sme=$server_host;
		$server_sme_api='http://'.$server_sme.'/api/';
	}
}
#print "server_host=$server_host\n";
#print "server_sme=$server_sme\n";
#print "server_sme_api=$server_sme_api\n";
#exit;

if($needSync==1){
	binmode STDOUT, ':utf8';

	if(defined $ARGV[0] && $ARGV[0] eq "get_gmt_offset"){
		my @t=localtime(time);
		my $gmt_offset_in_seconds = timegm(@t) - timelocal(@t);
		$gmt_offset_in_seconds-=3600 if($t[8]==1);
		print $gmt_offset_in_seconds;
		exit;
	}

	if(defined $ARGV[0]){
		if(index($ARGV[1], ':')>0){
			$smelogin = substr($ARGV[1], 0, index($ARGV[1],':'));
			$smepassword = substr($ARGV[1], index($ARGV[1],':')+1);
			if(index($smelogin, 'base64-')==0 && index($smepassword, 'base64-')==0){
				$smelogin=MIME::Base64::decode(substr($smelogin, length("base64-")));
				$smepassword=MIME::Base64::decode(substr($smepassword, length("base64-")));
			}
		}
		if(index($ARGV[2], ':')>0 && length($ARGV[2])>1){
			$providerUser = substr($ARGV[2], 0, index($ARGV[2],':'));
			$providerPassword = substr($ARGV[2], index($ARGV[2],':')+1);
			if(index($providerUser, 'base64-')==0 && index($providerPassword, 'base64-')==0){
				$providerUser=MIME::Base64::decode(substr($providerUser, length("base64-")));
				$providerPassword=MIME::Base64::decode(substr($providerPassword, length("base64-")));
			}
		}else{
			$providerUser="";
			$providerPassword="";
		}

		if(index($ARGV[2], ':')>-1){
			splice(@ARGV, 2, 1);
		}
	}else{
		exit;
	}
=head1
print "smelogin=$smelogin\n";
print "smepassword=$smepassword\n";
print "providerUser=$providerUser\n";
print "providerPassword=$providerPassword\n";
=cut
	if( defined $ARGV[0] ){
		if($ARGV[0] eq "gettoken"){
			$token=signIn();
#open FF, ">> /2/libfusesme-perl/usr/share/sme_install_pack/SMESyncCenter/123.txt";
#print FF "token=$token\n";
#close FF;
#			print $token;
#			sendSyncResponse($token);
			sendSyncResponse($token ."|<response>token=$token</response>");
		}

		if($ARGV[0] eq "setproviderdata"){
			$token = signIn();
#			print $token ."|<response>success</response>";
			sendSyncResponse($token ."|<response>success</response>");
		}

		if($ARGV[0] eq "modifyfile"){
#print "xxx=>". $ARGV[0]. " 1> ". ($ARGV[1]). " 2> ". ($ARGV[2]). " 3> ". ($ARGV[3]). " 4> ". ($ARGV[4])."\n";
			if(defined $ARGV[2] && defined $ARGV[3] && defined $ARGV[4]){
				$token=$ARGV[2] if(defined $ARGV[2]);
				my $mFileId=$ARGV[3];
				my $mStr=MIME::Base64::decode($ARGV[4]);
#utf8::encode($mStr);
				if(length($mFileId)<1 || length($mStr)<1){
#					print $token.'|<respose>error</respose>';
					sendSyncResponse($token.'|<respose>error</respose>');
					exit;
				}

				my @p;
				$p[0]=$mFileId;
				$p[1]=$mStr;
				$p[2]='n';

				my $result = runFunction($token,'doModifyFile',@p);
				if(defined $result->{status} && $result->{status} eq "ok"){
#					print $token ."|<response>success</response>";
					sendSyncResponse($token ."|<response>success</response>");
					exit;
				}

#				print $token ."|<response>error</response>";
				sendSyncResponse($token ."|<response>error</response>");
			}
		}

		if($ARGV[0] eq "getlistoffiles"){
			$token=$ARGV[3] if(defined $ARGV[3]);

			if(defined $ARGV[2] && defined $ARGV[4] && defined $ARGV[5]){
#print "xxx=>". $ARGV[0]. " 1> ". ($ARGV[1]). " 2> ". ($ARGV[2]). " 3> ". ($ARGV[3]);
				my $truncated=getListOfFiles($ARGV[2], $ARGV[4], $ARGV[5]);
				my $i=0;
				my $res="<response>";
				while( (defined $filelist[$i]) && (defined $filelist[$i][1]) ){
					my $name=$filelist[$i][0];
					utf8::encode($name);
					$name=MIME::Base64::encode($name);
					$name=~ s/\n//;
#print $name."\n";
					$res.='<f>';
					$res.='<id>'. $filelist[$i][1] .'</id>';
					$res.='<pid>'. $filelist[$i][2] .'</pid>';
					$res.='<name>'. XMLEscape($name) .'</name>';
					$res.='<type>'. $filelist[$i][7] .'</type>';
					if( (!defined $filelist[$i][15]) || ($filelist[$i][15]<2) || ($filelist[$i][15]==946677600) ){
						$filelist[$i][15]=$filelist[$i][5];
					}
					$res.='<lmodify>'. $filelist[$i][15] .'</lmodify>';
					$res.='</f>';
					$i++;
				}
				$res.="</response>";
				if($truncated==1){
					$truncated="[1]";
				}else{
					$truncated="[0]";
				}
#				print $token . "|". $truncated . $res;
				sendSyncResponse($token . "|". $truncated . $res);
			}
		}

		if($ARGV[0] eq "getdefaultprovider"){
			if(defined $ARGV[2]){
				$token=$ARGV[2];
			}

			my $res="<response>". getdefaultprovider() ."</response>";
#			print $token . "|". $res;
			sendSyncResponse($token . "|". $res);
		}

		if(($ARGV[0] eq "getuserbackgroundtasks")){
			if(defined $ARGV[2]){
				$token=$ARGV[2];
			}

#			my $res=getuserbackgroundtasks();
			my $res="<response>". getuserbackgroundtasks() ."</response>";
#			print $token . "|". $res;
			sendSyncResponse($token . "|". $res);
		}

		if($ARGV[0] eq "doprovidersyncinbackground"){
			$token=$ARGV[2] if(defined $ARGV[2]);

			if(defined $ARGV[3]){
				my $res="<response>". doprovidersyncinbackground($ARGV[3]) ."</response>";
#				print $token . "|". $res;
				sendSyncResponse($token . "|". $res);
			}
		}

		if($ARGV[0] eq "getfileslist"){
			$token=$ARGV[3] if(defined $ARGV[3]);

			if(defined $ARGV[2]){
#print "xxx=>". $ARGV[0]. " 1> ". ($ARGV[1]). " 2> ". ($ARGV[2]). " 3> ". ($ARGV[3]);
				loadFileTree($ARGV[2]);

				my $i=0;
				my $res="<response>";
				while(defined $filelist[$i] && defined $filelist[$i][1]){
					$res.='<f>';
					$res.='<id>'. $filelist[$i][1] .'</id>';
					$res.='<pid>'. $filelist[$i][2] .'</pid>';
					$res.='<name>'. XMLEscape($filelist[$i][0]) .'</name>';
					$res.='<type>'. $filelist[$i][7] .'</type>';
					if( (!defined $filelist[$i][15]) || ($filelist[$i][15]<2) || ($filelist[$i][15]==946677600) ){
						$filelist[$i][15]=$filelist[$i][5];
					}
					$res.='<lmodify>'. $filelist[$i][15] .'</lmodify>';
					$res.='</f>';
					$i++;
				}
				$res.="</response>";

#				print $token . "|". $res;
				sendSyncResponse($token . "|". $res);
			}
		}

		if(($ARGV[0] eq "createfolder")){
			if(defined $ARGV[2]){
				$token=$ARGV[2];
			}

			if(defined $ARGV[3] && defined $ARGV[4] && defined $ARGV[5] && defined $ARGV[6]){
				my $name=$ARGV[3];
				my $pid=$ARGV[4];
				my $structType=$ARGV[5];
				my $descr=$ARGV[6];

				$name = MIME::Base64::decode($name);
#utf8::decode($name);
#utf8::encode($name);
=head2
print "name=$name\n";
#utf8::encode($name);
print "name=$name\n";
#utf8::encode($name);
print "name=$name\n";
exit;
=cut
				if(length($descr)>0){
					$descr = MIME::Base64::decode($descr);
#utf8::encode($descr);
					$descr = substr($descr, 1);
				}else{
					$descr = '';
				}
				smestorage_mkdir($name, $pid, $structType, $descr);


#print "xxx=>". $ARGV[0]. " 1> ". ($ARGV[1]). " 2> ". ($ARGV[2]). " 3> ". ($ARGV[3]). " 4> ". ($ARGV[4]);			
#print " name ". $name. " pid> ". $pid;
#exit;

				my $i=0;
				my $res="<response>";
				while( (defined $filelist[$i]) && (defined $filelist[$i][1]) ){
					$res.='<f>';
					$res.='<id>'. $filelist[$i][1] .'</id>';
					$res.='<pid>'. $filelist[$i][2] .'</pid>';
					$res.='<name>'. XMLEscape($filelist[$i][0]) .'</name>';
					$res.='<type>'. $filelist[$i][7] .'</type>';
					if( (!defined $filelist[$i][15]) || ($filelist[$i][15]<2) || ($filelist[$i][15]==946677600) ){
						$filelist[$i][15]=$filelist[$i][5];
					}

					$res.='<lmodify>'. $filelist[$i][15] .'</lmodify>';
					$res.='</f>';
					$i++;
				}
				$res.="</response>";

#				print $token . "|". $res;
				sendSyncResponse($token . "|". $res);
			}
		}

		if($ARGV[0] eq "deletefile"){
			$token=$ARGV[2] if(defined $ARGV[2]);

			if(defined $ARGV[3] && defined $ARGV[4]){
				my $id=$ARGV[3];
				my $type=$ARGV[4];
				my $res="<response>";
				if( $type==0 ){
					if(smestorage_unlink("", $id)==0){
						$res.="Success";
					}else{
						$res.="Fail";
					}
				}else{
					if(smestorage_rmdir("", $id)==0){
						$res.="Success";
					}else{
						$res.="Fail";
					}
				}

				$res.="</response>";

#				print $token . "|". $res;
				sendSyncResponse($token . "|". $res);
			}
		}

		if($ARGV[0] eq "checkpathexists"){
			if(defined $ARGV[2]){
				$token=$ARGV[2];
			}

			if(defined $ARGV[3]){
				my $pp1=MIME::Base64::decode($ARGV[3]);
#utf8::encode($pp1);
				my $res="<response>". checkPathExists($pp1) ."</response>";
#				print $token ."|". $res;
				sendSyncResponse($token ."|". $res);
			}
		}

		if($ARGV[0] eq "uploadfile"){
			$|=1;
=head1
my $xx=8*1024;					#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
my $lt=0;
my $nt=0;
for(my $i=0; $i<1000000;){
	$xx="[". $i ."]\n";
	for(my $k=0; $k<134*1024+1;$k++){
		$i=$i+0.05;
	}
	$xx="[". substr($i, 0, index($i, ".")) ."]\n";
	$nt=time();
	if($nt-$lt>=1){
		print $xx;
		$lt=$nt;
	}

}
print "asdasdasdasd";
#print "8966aecc9e9c0c36421de201611be9ef|<response><f><id>6120773</id><pid>6120769</pid><name>123456.txt</name><type>0</type><lmodify>1284624362</lmodify></f></response>";
exit;
=cut
			$token=$ARGV[2] if(defined $ARGV[2]);

			if(defined $ARGV[3] && defined $ARGV[4] && defined $ARGV[5] && defined $ARGV[6] && defined $ARGV[7]){
				my $name=$ARGV[3];
				my $path=$ARGV[4];
				my $ulocaltime=$ARGV[5];
				my $pid=$ARGV[6];
				my $id=$ARGV[7];

				$name=MIME::Base64::decode($name);
				$path=MIME::Base64::decode($path);
#utf8::encode($path);
#				utf8::decode($path);
#utf8::encode($name);
#				utf8::decode($name);
#				utf8::decode($name);

				my $fileData;
				if(defined $path && length($path)>0){
					if(-e($path)){
						if(!open($fileData, "< ".$path)){
#							print $token ."|<response>permission=0</response>";
							sendSyncResponse($token ."|<response>permission=0</response>");
							exit;
						}
						close $fileData;
					}else{
#						print $token ."|<response></response>";
						sendSyncResponse($token ."|<response></response>");
						exit;
					}
				}else{
#					print $token ."|<response></response>";
					sendSyncResponse($token ."|<response></response>");
					exit;
				}

				my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$fsize,$atime,$mtime,$ctime,$blksize,$blocks) = stat($path);

#				if($fsize<1){
#					print $token ."|<response></response>";
#					exit;
#				}

				smestorage_flush($name, $path, $ulocaltime, $pid);				# $id - ???


#print "xxx=>". $ARGV[0]. " 1> ". ($ARGV[1]). " 2> ". ($ARGV[2]). " 3> ". ($ARGV[3]). " 4> ". ($ARGV[4]);			
#print " name ". $name. " pid> ". $pid;
#exit;

				my $i=0;
				my $res="<response>";
				while(defined $filelist[$i] && defined $filelist[$i][1]){
					$res.='<f>';
					$res.='<id>'. $filelist[$i][1] .'</id>';
					$res.='<pid>'. $filelist[$i][2] .'</pid>';
					$res.='<name>'. XMLEscape($filelist[$i][0]) .'</name>';
					$res.='<type>'. $filelist[$i][7] .'</type>';

					$filelist[$i][15]=$filelist[$i][5] if(!defined $filelist[$i][15] || $filelist[$i][15]<2 || $filelist[$i][15]==946677600);

					$res.='<lmodify>'. $filelist[$i][15] .'</lmodify>';
					$res.='</f>';
					$i++;
				}
				$res.="</response>";
#print Dumper(@filelist)."\n";			#!!!!!!!!!!!!!!!!!
				print $token . "|". $res;
				sendSyncResponse($token . "|". $res);
			}
		}

		if($ARGV[0] eq "downloadfile"){
			if(defined $ARGV[2]){
				$token=$ARGV[2];
			}

			if(defined $ARGV[3] && defined $ARGV[4] && defined $ARGV[5]){
				my $fullname=$ARGV[3];
				my $id=$ARGV[4];
				my $time=$ARGV[5];
				$fullname = MIME::Base64::decode($fullname);
#utf8::encode($fullname);

				my $fileData;
				if(defined $fullname && length($fullname)>0){
					if(!open($fileData, "> ".$fullname)){
#						print $token ."|<response>permission=0</response>";
						sendSyncResponse($token ."|<response>permission=0</response>");
						exit;
					}
					close $fileData;
				}

				my $res='<response>';
				if(smestorage_save_file_to_local($id, $fullname)==0){
					my @fl;
					$fl[0]=$fullname;
					utime $time, $time, @fl;
					$res .= 'Success';
				}else{
					$res .= 'Fail';
				}
				$res .= '</response>';
#				print $token . "|". $res;
				sendSyncResponse($token . "|". $res);
			}
		}

		exit;


	}else{
		exit;
	}

	exit;
}

if(defined $ARGV[0]){
	if($ARGV[0] eq "-h" || $ARGV[0] eq "--help"){
    print join "", <DATA>;
    exit(0);
	}
	$mountpoint = $ARGV[0];
	if(substr($mountpoint, 0, 1) eq '"' && substr($mountpoint, length($mountpoint)-1, 1) eq '"'){
		$mountpoint = substr($mountpoint, 1, length($mountpoint)-2);
	}
}

if( defined $ARGV[1] ){
	if($ARGV[1] eq "-h" || $ARGV[1] eq "--help"){
    print join "", <DATA>;
    exit(0);
	}
	
	if(index($ARGV[1], ':')>0){
		$smelogin = substr($ARGV[1], 0, index($ARGV[1],':'));
		$smepassword = substr($ARGV[1], index($ARGV[1],':')+1);
		if(index($smelogin, 'base64-')==0 && index($smepassword, 'base64-')==0){
			$smelogin=MIME::Base64::decode(substr($smelogin, length("base64-")));
			$smepassword=MIME::Base64::decode(substr($smepassword, length("base64-")));
		}
	}
}

if(defined $ARGV[2]){
	if(index($ARGV[2], ':')>0){
		$providerUser = substr($ARGV[2], 0, index($ARGV[2],':'));
		$providerPassword = substr($ARGV[2], index($ARGV[2],':')+1);
		if(index($providerUser, 'base64-')==0 && index($providerPassword, 'base64-')==0){
			$providerUser=MIME::Base64::decode(substr($providerUser, length("base64-")));
			$providerPassword=MIME::Base64::decode(substr($providerPassword, length("base64-")));
		}
	}
}

if(!defined $mountpoint || length($mountpoint)<1){
  print join "", <DATA>;
  exit(0);
}


if($printToLogFile==1){
	open(STDOUT, ">".$ENV{HOME}."/.smestorage.log");
	open(STDERR, ">&STDOUT");
	print " ";
#sleep(20);
}

if(!defined $smelogin || !defined $smepassword){			# Get login and password
=head1
	syswrite STDOUT, "Login: ";
	ReadMode('restore');
	$smelogin = ReadLine(0);
	chomp($smelogin);
	
	syswrite STDOUT, "Password: ";
	ReadMode('noecho');
	$smepassword = ReadLine(0);
	chomp($smepassword);
	ReadMode('restore');
	print "\n";
=cut
	if( $DEBUG>0 ){		print "Error. Login or password not found!\n";	}
	exit;
}


$token = signIn();

sub signIn{
	my $l=shift;
	my $p=shift;
	$l=$smelogin if(!defined $l);
	$p=$smepassword if(!defined $p);

	if($startSignIn>0){
		if( $DEBUG>0 ){		print "Error. Wrong login info\n";	}
#		print "login_fail" if($needSync==1);
		sendSyncResponse("<response>login_fail</response>") if($needSync==1);
		exit;
	}
	$startSignIn++;

	my $L=0;
	my $res;
	if( !($needSync==1 && length($token)>1) ){
		my @param;
		$param[0]=$l;
		$param[1]=$p;

		$res = runFunction('*', 'gettoken', @param);

		if(!defined $res->{status}){
			if( $DEBUG>0 ){		print "Error. Server not return \"status\"\n";	}
			exit;
		}

		if( (index($res->{status}, 'ok')==-1) || !(defined $res->{token}) ){
			if( $DEBUG>0 ){		print "Error. Unable to authenticate. Check Password.\n";	}
			exit;
		}
		$token = $res->{token};
	}else{
		$L=1;
	}

	if((defined $res->{notice} && $res->{notice}==1) || $L==1){			# need enter providerLogin and providerPassword
		if(($needSync==1 || $printToLogFile) && (length($providerUser)<1 || length($providerPassword)<1)){
			if($needSync==1){
#				print $token ."|notice=1\n";
				sendSyncResponse($token ."|<response>notice=1</response>\n");
				exit;
			}
			print "notice=1\n";
			exit;
		}else{
			if(length($providerUser)<1 || length($providerPassword)<1 ){
				if($needSync!=1 && $printToLogFile==0){
					if( $DEBUG>0 ){		print "Error. Provider login or provider password not found!\n";	}
					exit;
=head2
					syswrite STDOUT, "Provider Login: ";
					ReadMode('restore');
					$providerUser = ReadLine(0);
					chomp($providerUser);
	
					syswrite STDOUT, "Provider Password: ";
					ReadMode('noecho');
					$providerPassword = ReadLine(0);
					chomp($providerPassword);
					ReadMode('restore');
					print "\n";
=cut
				}
			}

			if(length($providerUser)>0 && length($providerPassword)>0){
				my @param;
				$param[0]=$providerUser;
				$param[1]=$providerPassword;

				my $ress = runFunction($token, 'setProviderData', @param);
#$ress->{status}="error"; #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
				if(  !( defined $ress->{status} ) ){
					if($needSync==1){
#						print $token ."|<response>error</response>";
						sendSyncResponse($token ."|<response>error</response>");
						exit;
					}
					if( $DEBUG>0 ){		print "Error. Server not return \"status\"\n";	}
					exit;
				}

				if( ($ress->{status} eq "login_token_expired") || ($ress->{status} eq "login_fail") ){
					$token = "*";
					return signIn();
				}

				if($ress->{status} ne "ok"){
					if($needSync==1){
#						print $token ."|<response>wrong_provider_info</response>";
						sendSyncResponse($token ."|<response>wrong_provider_info</response>");
						exit;
					}

					print "Error. Check your Provider login and Provider password!\n";
					exit;
				}

			}
		}

	}

	$startSignIn--;

	return $token;
}
# -------------------------------------------------------------------------------------------------------------
sub loadConfig{
	my $file;
	my $path=$ENV{HOME}.'/.smestorage.conf';

	if(!(-e $path) || (-s $path)<1){	#>
		saveConfig();
		return 0;
	}

	return -1 if(!open($file, "< ".$path));
	my $r='';
	my $p='';
	read($file, $r, -s $path);	#;
	close $file;
	$r.="\n";
	$r=~s/\r//gi;
#	$p=getPartofStr($r, 'host=', "\n");
	$p=$1 if($r=~/host=(.*?)\n/si);
	if(length($p)>0){
		$server_sme=$p;
		$server_sme_api='http://'.$server_sme.'/api/';
	}


#	$p=getPartofStr($r, 'SMALL_FILE_SIZE=', "\n");	
	if($r=~/SMALL_FILE_SIZE=([0-9]+)\n/si){
		$p=$1;
		$SMALL_FILE_SIZE=$p if(length($p)>0);
	}

#print "SMALL_FILE_SIZE=$SMALL_FILE_SIZE\n";
#print "server_sme=$server_sme\n";
#print "server_sme_api=$server_sme_api\n";
#print "r=$r\n";
	return 0;
}
# -------------------------------------------------------------------------------------------------------------
sub saveConfig{
	my $path=$ENV{HOME}.'/.smestorage.conf';

	my $L=0;
	$L=1 if(!(-e $path) || (-s $path)<1);	#>


	if(open(FF, "> ".$path)){
		if($L==1){
			my $r="";
			$r.="host=$server_sme\n";
			$r.="SMALL_FILE_SIZE=$SMALL_FILE_SIZE\n";
			print FF $r;
		}
		close FF;
	}
}
# -------------------------------------------------------------------------------------------------------------
sub sendSyncResponse{
	my $response=shift;
	my $path="";

	for(my $i=0; defined $ARGV[$i]; $i++){
		if(index($ARGV[$i], "--tofile=")!=-1 && !defined $ARGV[$i+1]){
			$path=substr($ARGV[$i], index($ARGV[$i], "--tofile=")+9);
			last;
		}
	}

	my $L=0;
	if(defined $path && length($path)>0 && (-e $path)){
		if(open(FF, "> ".$path)){
			print FF $response;
			close FF;
		}
	}else{
		print $response;
	}
#	exit;
}
# -------------------------------------------------------------------------------------------------------------
sub getPartofStr{
	my ($st, $st1, $st2)=@_;
	my $res='';
	my $s=index($st, $st1);
	if($s>0){
		$s+=length($st1);
		my $e=index($st, $st2, $s);
		$res=substr($st, $s, $e-$s) if($e>0);
	}
	return $res;
}
# -------------------------------------------------------------------------------------------------------------
sub getdefaultprovider {
	my @p;
	$p[0]="";
	my $result = runFunction($token,'getProviders',@p);
	return $result->{defaultprovider} if(defined $result->{defaultprovider});

	return -1;
}
# -------------------------------------------------------------------------------------------------------------
sub XMLEscape{
	my $m=shift;
	$m =~ s/&/&amp;/g;
	$m =~ s/</&lt;/g;
	$m =~ s/>/&gt;/g;
	$m =~ s/"/&quot;/g;
	$m =~ s/'/&apos;/g;
	return $m;
}
# -------------------------------------------------------------------------------------------------------------
sub doprovidersyncinbackground {
	my $id=shift;
	my @p;
	$p[0]=$id;
	my $result = runFunction($token,'doProviderSyncInBackground',@p);
	return $result->{taskid} if(defined $result->{taskid});

	return -1;
}
# -------------------------------------------------------------------------------------------------------------
sub getuserbackgroundtasks {
	my @p;
	$p[0]="";
	my $result = runFunction($token,'getUserBackgroundTasks',@p);
	my $res="";

	if( defined $result->{tasks}  ){
		my $k=0;
		while(defined $result->{tasks}->{"n".$k}->{bt_id}){
			if(defined $result->{tasks}->{"n".$k}->{bt_status}){
				$res.="<t><id>". $result->{tasks}->{"n".$k}->{bt_id} ."</id><status>". XMLEscape($result->{tasks}->{"n".$k}->{bt_status}) ."</status></t>";
			}

			$k++;
		}
	}

	return $res;
}

my $lastUpdateMainFolder=0;

#####################################################################################################
# subs
#####################################################################################################

# load HTML page
sub get_page {
	my $path=shift;
	my $method=shift;
	$method="GET" unless defined $method;
	my $browser = LWP::UserAgent->new();
	$browser->agent('FuseSMEStorage '.$VERSION);
	my $response;
	if(  $response = $browser->get($path)  ){
		$response = $response->content;
	}
	
	return $response;
}

#####################################################################################################

# Run SME funcrion
sub runFunction {
	my ($tkn, $name, @p) = @_;
	my $furl=$server_sme_api;
#	if( $DEBUG>0 ){		print "_runFunction($tkn, $name, array())\n";	}
	$lastRequestToServer=time();

	$furl.=$tkn.'/';
	$furl.=$name.'/';
	my $L=0;
	my $i;
	for $i (0 .. $#p){
		$p[$i]='' if(!defined($p[$i]));

		if($L!=0){
			$furl.=',';
		}else{
			$L=1;
		}

#		my $xe=MIME::Base64::encode($p[$i]);
		my $xe='';

my $m=$p[$i];
#utf8::decode($m);
my $L=1;
eval { $m=MIME::Base64::encode($m); $L=0; };
if($L){
	utf8::encode($m);
	eval { $m=MIME::Base64::encode($m); $L=0; };
}

$m='' if($L);
$xe=$m;

#		$xe=~ s/\+/%2B/g;		
		$furl = $furl . $xe;
	}
	$furl=~ s/\n//g;

	my $res = get_page($furl);
	my $data;

	if(defined $res){
		if($name eq "getFile"){
			$data=$res;
		}else{
			if(index($res, '<response>' ) > -1){
				my $simple=XML::Simple->new();
				$data=$simple->XMLin($res);
			}

			if(defined $data->{status}  &&  ($data->{status} eq "login_token_expired" || $data->{status} eq "login_fail")){
				$token="*";
				$token=signIn();
				my $result = runFunction($token, $name, @p);
				return $result;
			}

		}
		return $data;
	}else{
		if( $DEBUG>0 ){		print "Can't connect to server   $server_sme_api\n";	}
		exit;
	}
}

#####################################################################################################

sub getFileId	{
	my $dir=shift;
	my $returnShadow=shift;
	my $notIgnoreShadow=shift;
	if($DEBUG>4){		print "_getFileId($dir)\n";	}

	if($dir eq '/'){
		return 0;
	}

	$dir=$dir.'/' if(index($dir, '/', length($dir))<0);
	
	my $folder_id=0;
	my $n=index($dir, '/');
	while($n>-1){
		my $folder_name = substr($dir, $n+1, index($dir, '/', $n+1) - ($n+1));
	
		if(length($folder_name)>0){
			my $k=0;
			my $L=0;
			while (defined $filelist[$k][0]){
				my $mmm=$filelist[$k][0];
				my $mmm2=$folder_name;
				utf8::encode($mmm2);
				utf8::decode($folder_name);

#print "if(".$mmm." eq $folder_name && $folder_id==".$filelist[$k][2]."){\n";
				if(($mmm eq $folder_name || $mmm eq $mmm2 || $filelist[$k][0] eq $folder_name) && $folder_id==$filelist[$k][2]){
					$L=1;
					$folder_id = $filelist[$k][1];
					if(defined $filelist[$k][13] && $filelist[$k][13] eq '1'){
						return (-7654321, $folder_id) if(defined $returnShadow && $returnShadow==1);
						return $folder_id if(defined $notIgnoreShadow && $notIgnoreShadow==1);
						return ;
					}

					last;
				}
				$k++;
			}

			if($L==0){
				if($DEBUG>4){		print "Folder or file not found.\n";	}
				return;
			}
		}

		$n=index($dir, '/', $n+1);
	}

	if(!defined $folder_id){
		return;
	}

	return $folder_id;
}

#####################################################################################################

sub checkPathExists{
	my $dir = shift;

	return 0 if($dir eq '/');

	my $folder_id=0;
	$dir=substr($dir, 1) if(index($dir, '/')==0);
	my @p;
	$p[0]=$dir;
	$p[1]='0';
	
	my $result = runFunction($token, 'checkPathExists', @p);
	
	if(! defined $result->{status}){
		if( $DEBUG>0 ){		print "Error. Server not return \"status\"\n";	}
		exit;
	}

	if(!defined $result->{objectid}){
		$folder_id=-1;
	}else{
		$folder_id=$result->{objectid};
	}

	return $folder_id;
}

#####################################################################################################

sub loadFileTree {
	my $folder_id=shift;
	$folder_id='' if(!defined $folder_id);

	my @p;
	$p[0]=$folder_id;			# fi_id
	$p[1]='0';						# limitlonglists
	$p[2]='y';						# ignorefilter
	
	my $result = runFunction($token, 'getFilesList', @p);
	
	if(!defined $result->{status}){
		if( $DEBUG>0 ){		print "Error. Server not return \"status\"\n";	}
		exit;
	}
	
	if(!defined $result->{filelist}){
		return 0;
	}

	my @delElements;
	my $i=0;
	my $i2=0;
	if(defined $folder_id && length($folder_id)>0){
		while(defined $filelist[$i][0]){		# delete old elements from array
			if($filelist[$i][2]==$folder_id && (!defined $filelist[$i][13] || !($filelist[$i][13] eq '1'))  ){
				$delElements[$i2][0]=$filelist[$i][0];
				$delElements[$i2][1]=$filelist[$i][1];
				$delElements[$i2][2]=$filelist[$i][2];
				$delElements[$i2][3]=$filelist[$i][3];
				$delElements[$i2][4]=$filelist[$i][4];
				$delElements[$i2][5]=$filelist[$i][5];
				$delElements[$i2][6]=$filelist[$i][6];
				$delElements[$i2][7]=$filelist[$i][7];
				$delElements[$i2][8]=$filelist[$i][8];
				$delElements[$i2][9]=$filelist[$i][9];
				$delElements[$i2][10]=$filelist[$i][10];
				$delElements[$i2][11]=$filelist[$i][11];
				$delElements[$i2][12]=$filelist[$i][12];
				$delElements[$i2][13]=$filelist[$i][13];
				$delElements[$i2][14]=$filelist[$i][14];
				$i2++;
				splice (@filelist, $i, 1);
				next;
			}

			$i++;
		} # while
		
		$i=0;
		if($folder_id==0){
			$lastUpdateMainFolder=time();
		}else{
			while(defined $filelist[$i][0]){
				if($filelist[$i][1]==$folder_id){
					$filelist[$i][12] = time();					# last update
					last;
				} # if
				$i++;
			} # while
		} # if

	} # if


	if(defined $filelist[0][0]){
		$i=$#filelist;
	}else{
		$i=0;
	}

	my $j=0;
	while(defined $result->{filelist}->{'n'.$j}->{fi_name}){		# add new elements to array
		if($result->{filelist}->{'n'.$j}->{fi_encrypted}==1 && $needSync!=1){
			$j++;
			next;
		}

		$filelist[$i][0]=$result->{filelist}->{'n'.$j}->{fi_name};		# name
		$filelist[$i][1]=$result->{filelist}->{'n'.$j}->{fi_id};			# id
		$filelist[$i][2]=$result->{filelist}->{'n'.$j}->{fi_pid};			# parent_id

		$filelist[$i][3]=dateToUnix($result->{filelist}->{'n'.$j}->{fi_created});			# fi_created
		$filelist[$i][4]=dateToUnix($result->{filelist}->{'n'.$j}->{fi_lastaccessed});# fi_lastaccessed
		$filelist[$i][5]=dateToUnix($result->{filelist}->{'n'.$j}->{fi_modified});		# fi_modified
		$filelist[$i][6]=$result->{filelist}->{'n'.$j}->{fi_size};					# fi_size
		$filelist[$i][7]=$result->{filelist}->{'n'.$j}->{fi_type};					# fi_type   0 -file; 1- folder

		if(defined $result->{filelist}->{'n'.$j}->{fi_description} && index($result->{filelist}->{'n'.$j}->{fi_description}, "HASH(0x")!=0){
			$filelist[$i][8]=$result->{filelist}->{'n'.$j}->{fi_description};		# fi_description
		}else{
			$filelist[$i][8]="";
		}

		if(defined $result->{filelist}->{'n'.$j}->{fi_tags} && index($result->{filelist}->{'n'.$j}->{fi_tags}, "HASH(0x")!=0){
			$filelist[$i][9]=$result->{filelist}->{'n'.$j}->{fi_tags};		# fi_tags
		}else{
			$filelist[$i][9]="";
		}

		$filelist[$i][10]='';							# content
		$filelist[$i][11]='';							# cache
		$filelist[$i][12]='';							# last update
		$filelist[$i][13]='0';						# shadow file
		$filelist[$i][14]='';							# cache for one read

		if(defined $result->{filelist}->{'n'.$j}->{fi_localtime}){
			$filelist[$i][15]=dateToUnix($result->{filelist}->{'n'.$j}->{fi_localtime});		# fi_localtime
		}else{
			$filelist[$i][15]=1;
		}
		$filelist[$i][16]=0;		# 1 - fi_size is unknow or 0

#=head1
		if($needSync!=1){
			utf8::encode($filelist[$i][0]);
			utf8::encode($filelist[$i][8]);
			utf8::encode($filelist[$i][9]);
		}
#=cut

		my $j2=0;
		my $x_id=$result->{filelist}->{'n'.$j}->{fi_id};
		while(defined $delElements[$j2][1]){
			if($delElements[$j2][1]==$x_id){
				$filelist[$i][10]=$delElements[$j2][10] if(defined $delElements[$j2][10] && length($delElements[$j2][10])>0 && $delElements[$j2][10] ne ' ');
				$filelist[$i][14]=$delElements[$j2][14] if(defined $delElements[$j2][14] && length($delElements[$j2][14])>0);
				last;
			}
			$j2++;
		}

		# need use this method because FUSE gets size of file and then download file, but if size of file is 0 then FUSE get only 0 
		my $elm=$result->{filelist}->{'n'.$j};	
		if($filelist[$i][7]==0 && (length($filelist[$i][6])<1 || 1*$filelist[$i][6]<1)){
			$filelist[$i][11]=getDesktopFileById($filelist[$i][1]);
			$filelist[$i][6]=length($filelist[$i][11]);
			$filelist[$i][0]=$filelist[$i][0].'.desktop';
			$filelist[$i][16]=1;
		}

		$i++;
		$j++;
	}
}

#####################################################################################################

sub getDesktopFileById {
	my $id=shift;
#print "last request ". (time()-$lastRequestToServer) ."s ago.\n";
	if(time()-$lastRequestToServer>1800){
		$token='*';
		$token=signIn();
=head1
# need change file name, but FUSE gets file using name by parts, so it will be error. and FUSE already have size of file? it also is problem.
		my $result=runFunction($token, 'getFileFullInfo', ($id));
		if(defined $result->{file} && defined $result->{file}->{fi_size} && length($result->{file}->{fi_size})>0 && $result->{file}->{fi_size}>0){
			for(my $i=0; defined $filelist[$i][1]; $i++){
				if($filelist[$i][1]==$id){
					$filelist[$i][6]=$result->{file}->{fi_size};
					$filelist[$i][11]='';
					$filelist[$i][16]=0;
					return '';
				}
			}
		}
=cut
	}

	my $durl=$server_sme_api .$token.'/getFile/'. MIME::Base64::encode($id) .',';
	$durl=~ s/\n//g;
	return "[Desktop Entry]\nType=Link\n\nURL=". $durl;
}

#####################################################################################################

sub getListOfFiles {
	my $folder_id=shift;
	my $start=shift;
	my $count=shift;

	$folder_id='' if(!defined $folder_id);
	$start="0" if(!defined $start || $start eq "");

	my @p;
	$p[0]=$start;
	$p[1]="id";
	$p[2]=$count;
	$p[3]='y';
	$p[4]=$folder_id;

	my $result = runFunction($token, 'getListOfFiles', @p);

	if(!defined $result->{status}){
		if( $DEBUG>0 ){		print "Error. Server not return \"status\"\n";	}
		exit;
	}
	
	if(!defined $result->{files}){
		return 0;
	}

	my $i=0;
	if(defined $filelist[0][0]){
		$i=$#filelist;
	}else{
		$i=0;
	}

	my $j=0;
	while(defined $result->{files}->{'n'.$j}->{fi_name}){				# add new elements to array
		$filelist[$i][0]=$result->{files}->{'n'.$j}->{fi_name};		# name
		$filelist[$i][1]=$result->{files}->{'n'.$j}->{fi_id};			# id
		$filelist[$i][2]=$result->{files}->{'n'.$j}->{fi_pid};		# parent_id

		$filelist[$i][3]=dateToUnix($result->{files}->{'n'.$j}->{fi_created});			# fi_created
		$filelist[$i][4]=dateToUnix($result->{files}->{'n'.$j}->{fi_lastaccessed});	# fi_lastaccessed
		$filelist[$i][5]=dateToUnix($result->{files}->{'n'.$j}->{fi_modified});			# fi_modified
		$filelist[$i][6]=$result->{files}->{'n'.$j}->{fi_size};					# fi_size
		$filelist[$i][7]=$result->{files}->{'n'.$j}->{fi_type};					# fi_type   0 -file; 1- folder

		if(defined $result->{files}->{'n'.$j}->{fi_description} && index($result->{files}->{'n'.$j}->{fi_description}, "HASH(0x")!=0){
			$filelist[$i][8]=$result->{files}->{'n'.$j}->{fi_description};		# fi_description
		}else{
			$filelist[$i][8]="";
		}

		if(defined $result->{files}->{'n'.$j}->{fi_tags} && index($result->{files}->{'n'.$j}->{fi_tags}, "HASH(0x")!=0){
			$filelist[$i][9]=$result->{files}->{'n'.$j}->{fi_tags};		# fi_tags
		}else{
			$filelist[$i][9]="";
		}

		$filelist[$i][10]='';			# content
		$filelist[$i][11]='';			# cache
		$filelist[$i][12]='';			# last update
		$filelist[$i][13]='0';		# shadow file
		$filelist[$i][14]='';			# cache for one read
#		$filelist[$i][16]='';			# maincode . extension
		$filelist[$i][17]=0;			# is empty file

		if(defined $result->{files}->{'n'.$j}->{fi_localtime}){
			$filelist[$i][15]=dateToUnix($result->{files}->{'n'.$j}->{fi_localtime});		# fi_localtime
		}elsif(defined $filelist[$i][5] && $filelist[$i][5] ne ""){
			$filelist[$i][15]=$filelist[$i][5];
		}else{
			$filelist[$i][15]=1;
		}

		$i++;
		$j++;
	}

	if(defined $result->{truncated} && $result->{truncated}==1){
		return 1;
	}

	return 0;
}

#####################################################################################################

sub dateToUnix {
	my $date = shift;

	my ($mday, $mmon, $myear, $h, $m, $s);
	my $date01=substr($date, 0, index($date,' '));
	my $date02=substr($date, index($date,' '), length($date)-index($date,' '));
	
	my @date1=split('-', $date01);
	my @date2=split(':', $date02);

	$date1[1]=$date1[1]-1;
	$date1[1]=0 if($date1[1]<0 || $date1[1]>11);
	$date1[2]=1 if($date1[2]<1 || $date1[2]>31);

	my $time=timelocal($date2[2], $date2[1], $date2[0], $date1[2], $date1[1], $date1[0]);

	return $time;
}

#####################################################################################################

sub smestorage_getdir {
	my $dir=shift;

#utf8::encode($dir);

	if( $DEBUG>4 ){	print "_getdir($dir)\n";	}

	my $k=0;
	my $lastupdate;
	my $dir_id=getFileId($dir);
	if(defined $dir_id && length(''.$dir_id)<1){
		if($dir_id==0){
			$lastupdate = $lastUpdateMainFolder;
		}else{
			while(defined $filelist[$k][1]){
				$lastupdate=$filelist[$k][12] if($filelist[$k][1]==$dir_id);
				$k++;
			}
		}
	}

	if(defined $lastupdate && (length($lastupdate)>0) && ((time()-$lastupdate) < 3)){
																					# use cach
	}else{
		loadFileTree($dir_id);								# update cach
	}

	$dir_id = getFileId($dir);

	if(!defined $dir_id){
		return;
	}

	my @files;
	my $j=0;
	my $i=0;
	while(defined $filelist[$i][0]){
		if($filelist[$i][2]==$dir_id){
			$files[$j]=$filelist[$i][0];
			$j++;
		}
	
		$i++;
	}

	return (@files, 0);
}

# --------------------------------------------------------------------------------------------------------

sub smestorage_getattr {
	my $filename = shift;
	if( $DEBUG>4 ){	print "_getattr($filename)\n";	}
##utf8::encode($filename);
#	if( $DEBUG>4 ){	print "_getattr($filename)\n";	}

	if($filename eq "/*#.....................................#sme_mount_test_file"){
		return (0,0,33206,1,0,0,0,24580,841073887,841073887,841073887,1024,1);
	}

	my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);

	$dev=0;
	$ino=0;
	$nlink=1;

	$uid = $<;
	($gid) = split / /, $(;

	$rdev=0;
	$size=0;

	$blksize=1024;
	$blocks=1;

	my $file_id;# = getFileId($filename);
	my $n=-1;

	my $file_name=substr($filename, rindex($filename, '/')+1);
	my $parent_dir=substr($filename, 0, rindex($filename, '/')+1);

	my $parent_id=getFileId($parent_dir);

	my $type="100";
	my $modebits="777";

	if($filename eq "/"){
		$type=0040;
		$modebits=0777;
		$mode=($type << 9) + $modebits;
		my $now=time();

		$mode=0040777;
		return ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$now,$now,$now,$blksize,$blocks);
	}

	my $k=0;
	while (defined $filelist[$k][0]){
		my $mmm=$filelist[$k][0];
utf8::encode($mmm);

#		print "if(". $filelist[$k][0] ." eq $file_name && $parent_id==". $filelist[$k][2]."){\n";
		if(($filelist[$k][0] eq $file_name || $mmm eq $file_name) && $parent_id==$filelist[$k][2]){
			$n=$k;
			last;
		}
		$k++;
	}

	if(!defined $n || $n<0){
		if( $DEBUG>4 ){	print "_getattr() ===> not found\n";	}
		return -ENOENT();
	}

	my @files;
	$ctime=$filelist[$n][3];
	$atime=$filelist[$n][4];
	$mtime=$filelist[$n][5];

	$size=$filelist[$n][6];

	if($filelist[$n][7]==1){						# file type   0 -file; 1- folder
		$type = "40";
		$modebits = "777";
	}

	if($filelist[$n][7]==1){
		$mode = 0040777;
	}else{
		$mode = 0100777;
	}

	if(!defined $ctime){
		return -ENOENT();
	}else{
		return ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
	}
}

# -----------------------------------------------------------------------------------------------------------

sub smestorage_rename {
	my $oldname = shift;
	my $newname = shift;
	if( $DEBUG>4 ){	print "_rename($oldname, $newname)\n";	}
	my $resNewname=$newname;
	my $resOldname=$oldname;

	my ($file_id, $pwfile_id)=getFileId($oldname, 1);

	$newname=substr($newname, 0, length($newname)-1)  if(substr($newname, length($newname)) eq '/');
	my $newFolder=substr($newname, 0, rindex($newname, '/')+1);
	$newname = substr($newname, rindex($newname, '/')+1);

	$oldname=substr($oldname, 0, length($oldname)-1)  if(substr($oldname, length($oldname)) eq '/');
	my $oldFolder=substr($oldname, 0, rindex($oldname, '/')+1);
	$oldname = substr($oldname, rindex($oldname, '/')+1);
	my $oldFolderId = getFileId($oldFolder);

	my $needUpload=0;
	if(!defined $file_id || $file_id<1){
		if(defined $file_id && $file_id==-7654321 && defined $pwfile_id && $pwfile_id>0){			# need upload file
			$needUpload=1;
#			my ($file_id, $pwfile_id)=getFileId($resNewname, 1);

			return -1 if($newFolder ne $oldFolder);

			my $nfile_id=getFileId($resNewname);
			return -1 if(!defined $nfile_id || $nfile_id<1);

			my $nf=-1;
			my $of=-1;
			my $k0=0;
			while(defined $filelist[$k0][1]){
#print "if(".$filelist[$k0][1]."==1 && ".$filelist[$k0][2]."==".$oldFolderId." && ".$filelist[$k0][0]." eq $oldname);\n";
				$of=$k0 if($filelist[$k0][1]==1 && $filelist[$k0][2]==$oldFolderId && $filelist[$k0][0] eq $oldname);
				$nf=$k0 if($filelist[$k0][1]==$nfile_id);
				$k0++;
			}

			return -1 if($nf<0 || $of<0);

#print "of=".$of.";  nf=".$nf.";\n";


			$filelist[$nf][10]=$filelist[$of][10];
#			$filelist[$of][10]=$filelist[$nf][10];

=head1
print "================== OLD =================\n";
print "name= ".$filelist[$of][0]."\n";
print "data= ".$filelist[$of][10]."\n";
#print "data= ".$filelist[$of][10]."\n";
print "================== NEW =================\n";
print "name= ".$filelist[$nf][0]."\n";
print "data= ".$filelist[$nf][10]."\n";
#print "data= ".$filelist[$nf][10]."\n";
print "<================\n";
=cut
			smestorage_flush($resNewname);
			return 0;
		}else{
			if($DEBUG>4){		print "Error: file_id is not defined\n";	}
			return -EIO();
		}
	}
# #utf8::encode($mmm);

	my $newFolderId = getFileId($newFolder);

	if($newFolder ne $oldFolder){
		return -1 if(smestorage_moveFile($file_id, $newFolderId)!=0);
		return 0;
	}

	return 0 if($newname eq $oldname);

	my $i=0;
	my $L=0;
	my $desc;
	my $tags;
	while(defined $filelist[$i][1]){
		if($filelist[$i][1] == $file_id){
			$desc=$filelist[$i][8];
			$tags=$filelist[$i][9];

			$L=1;
			last;
		}
		$i++;
	}

	if($L==0){
		if($DEBUG>4){		print "error: file_id=$file_id; L=$L \n";	}
		return -ENOENT();
	}

	my @p;
	$p[0]=$file_id;		# fi_id
	$p[1]=$newname;		# fi_name
	$p[2]=$desc;			# fi_description
	$p[3]=$tags;			# fi_tags
	
	my $result=runFunction($token, 'doRenameFile', @p);
	$filelist[$i][0]=$newname if(defined $result->{status} && $result->{status} eq 'ok');

	return 0;
}

# -------------------------------------------------------------------------------------------------------------

sub smestorage_moveFile {
	my $file_id=shift;
	my $newFolderId=shift;

	if($DEBUG>4){	print "_moveFile($file_id, $newFolderId)\n";	}

	my $isFile=0;
	my $i=0;
	while(defined $filelist[$i][1]){
		if($filelist[$i][1]==$file_id){
			$isFile=1 if($filelist[$i][7]==0);
			last;
		}
		$i++;
	}

	my @p;
	$p[0]=$file_id;
	$p[1]=$newFolderId;

	my $result;
	if($isFile==0){
		$result=runFunction($token, 'doMoveFiles', @p);
	}else{
		$result=runFunction($token, 'doMoveFolders', @p);
	}

	if(defined $result->{status} && $result->{status} eq 'ok'){
		my $i=0;
		while(defined $filelist[$i][1]){
			if($filelist[$i][1]==$file_id){
				$filelist[$i][2]=$newFolderId;
				last;
			}
			$i++;
		}
	}

	return 0;
}
# -------------------------------------------------------------------------------------------------------------

sub smestorage_open {
	my $file = shift;
	my $flags = shift;

	return 0;
}

# -------------------------------------------------------------------------------------------------------------

sub smestorage_read {
	my $file = shift;
	my $size = shift;
	my $offset = shift;
	if( $DEBUG>4 ){	print "_read($file, $size, $offset)\n";	}

	my $file_id = getFileId($file);

	if(!defined $file_id || $file_id<1){
		if($DEBUG>4){		print "Error: file_id not found\n";	}
		return -EIO();
	}

	my $i=0;
	my $L=0;
	while(defined $filelist[$i][1]){
		if($filelist[$i][1]==$file_id){
			$L=1;
			last;
		}
		$i++;
	}

	$filelist[$i][11]=getDesktopFileById($filelist[$i][1]) if(defined $filelist[$i][16] && $filelist[$i][16]==1);	# size of file is unknow, need get .desktop code
	my $data='';
	if($L==1 && defined $filelist[$i][14] && (length($filelist[$i][14])>$offset+$size || length($filelist[$i][14])==$filelist[$i][6])){
		$data=$filelist[$i][14];														# Get from cache for one read
		$filelist[$i][14]='';																# clear cache for one read
	}else{
		if($L==1 && length($filelist[$i][11])>0){
			$data=$filelist[$i][11];													# Get from cache
		}else{
=head1
			if(rindex($file, '.desktop')==length($file)-8){
				$data="[Desktop Entry]\nType=Link\n\nURL=http://". $server_sme ."/files/".$filelist[$i][16];
			}else{
			}
=cut
			my @p1;
			$p1[0]=$file_id;
			$p1[1]='';

			$data=runFunction($token, 'getFile', @p1);
#			$filelist[$i][6]=length($data) if($filelist[$i][6]==0);
		}
	}

	$filelist[$i][11]=$data if($L==1);														# Save in cache
	return substr($data, $offset, $size);
}

# -------------------------------------------------------------------------------------------------------------
#=head1
sub smestorage_save_file_to_local{
	my $file_id=shift;
	my $path=shift;
	my $ext=shift;

	my $descr;
	open($descr, "> $path");

	my $furl='/getFile/'. MIME::Base64::encode($file_id) .',';
	$furl=~ s/\n//g;

	my ($res, $content_type) = ("", "");
	my $port="80";
	my $uri="/api/". $token . $furl;
	my $socket;
	my $content_length=0;
	my $r="";

	$uri = "/api/". $token . $furl;
	$socket = new IO::Socket::INET(		#open socket to the server
		PeerAddr => $server_sme,
		PeerPort => $port,
		Proto    => 'tcp');

	unless( $socket ){
		return -1;
	}

	$socket->autoflush(1);
	print $socket "GET $uri HTTP/1.1\r\nHost: $server_sme\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1;)\r\nAccept-Encoding: gzip, deflate\r\nConnection: Keep-Alive\r\n\r\n";

	my $sel=new IO::Select($socket);
	my @arr;
	my $timeout=10;
	while(@arr=$sel->can_read($timeout)){
		my $rr="";
		sysread($socket, $rr, 1);
		$r.=$rr;
#print "$rr";

		my $s01=index($r, "\n\n");
		my $s02=index($r, "\r\n\r\n");
		if($s01>0 || $s02>0){
			last;
		}
	}

	if(length($r)<1){
		$token=signIn();
		if(!defined $ext){
			return smestorage_save_file_to_local($file_id, $path, 1);
		}else{
			return -2;
		}
	}

	my $lt=0;
	my $nt=0;
	my $downloaded=0;
	if(length($r)>0){
		my $tr=$r;
		$tr=~ s/\r//g;
		my $s=index($tr, "Content-Length: ");
		if($s>-1){
			$s=$s+length("Content-Length: ");
			$content_length=substr($tr, $s, index($tr, "\n", $s)-$s);
		}

		if($content_length eq "" || $content_length<1){
			if(index(lc($tr), "transfer-encoding: chunked")>-1){
				my $browser = LWP::UserAgent->new();
				$browser->agent('FuseSMEStorage '.$VERSION);
				$|=1;
				sendSyncResponse("{0}\n");   # for sync
				$|=0;
				my $resp=$browser->get($server_sme_api .$token .$furl, ':content_file' => $path);
				return -1 unless $resp->is_success;
				return 0;
			}
		}

		$|=1;
#		print "{". $content_length ."}\n";   # for sync
		sendSyncResponse("{". $content_length ."}\n");   # for sync
		$|=0;

		$s=index($tr, "Content-Type: ");
		if($s>-1){
			$s=$s+length("Content-Type: ");
			$content_type=substr($tr, $s, index($tr, "\n", $s)-$s);
		}

		$s=index($r, "\n\n");								# delete headers
		my $s2=index($r, "\r\n\r\n");
		if($s>-1 && $s2>-1){
			if($s<$s2){
				$r=substr($r, $s+2);
			}else{
				$r=substr($r, $s2+4);
			}
		}else{
			if($s>-1){
				$r=substr($r, $s+2);
			}
			if($s2>-1){
				$r=substr($r, $s2+4);
			}
		}

		return -4 if($s<0 && $s2<0);

		print $descr $r;

		$|=1;
		$downloaded+=length($r);
#		print "[". $downloaded ."]\n";
		sendSyncResponse("[". $downloaded ."]\n");   # for sync
		$|=0;
	}

	return -3 if($content_length<1);

	my $L1=1;
	while($L1==1){
		$L1=0;
		$r="";
#print "try read from $downloaded => ";
		$|=1;
		my $n=1;
		$n=1024 if($downloaded+1024<=$content_length);
		$n=100 if($downloaded+100<=$content_length);
		$n=15 if($downloaded+15<=$content_length);

		sysread($socket, $r, $n);
#print "<= OK\n";
		if( length($r)>0 ){
			print $descr $r;
			$downloaded+=length($r);
			$nt=time();								# for sync
			if($nt-$lt>0){
				$|=1;
#				print "[". $downloaded ."]\n";
				sendSyncResponse("[". $downloaded ."]\n");
				$|=0;
				$lt=$nt;
			}
		}

		$L1=1 if(length($r)>0);
		$L1=0 if($downloaded>=$content_length);
	}

	$|=1;
#	print "[". $content_length ."]\n";   # for sync
	sendSyncResponse("[". $content_length ."]\n");
	$|=0;

	close $socket;
	close($descr);
	
  return 0;
}
#=cut
# -------------------------------------------------------------------------------------------------------------

sub smestorage_release {
	my $file = shift;
	my $flags = shift;

	return 0;
}

# -------------------------------------------------------------------------------------------------------------

sub smestorage_readlink {
	my $filename = shift;
	my $file_id = getFileId($filename);
	if( $DEBUG>4 ){	print "_readlink($filename)\n";	}

	return -EIO() if(!defined $file_id || $file_id==0);

	my $file_type;
	my $file_name;
	my $i=0;
	while(defined $filelist[$i][1]){
		if($filelist[$i][1] == $file_id){
			$file_type = $filelist[0][7];			# 0 -file; 1- folder
			$file_name = $filelist[$i][0];

			last;
		}
		$i++;
	}

	if(!defined $file_type){
		return return -EIO();
	}else{
		if($file_type==0){
			return $file_name;
		}else{
			return -EIO();
		}
	}

}

# -------------------------------------------------------------------------------------------------------------

sub smestorage_unlink {
	my $file = shift;
	my $file_id = shift;
	if( $DEBUG>4 ){	print "_unlink($file)\n";	}
	if(!defined $file_id || $file_id<=0){
		$file_id = getFileId($file);

		return -EIO() if(!defined $file_id || $file_id < 1);

		my @files;
		my $i=0;
		while(defined $filelist[$i][1]){
			if($filelist[$i][1]==$file_id){
				smestorage_rmdir($file) if($filelist[$i][7]==1);			# file type   0 -file; 1- folder
				splice (@filelist, $i, 1);

				last;
			}
			$i++;
		}
	}

	my @p;
	$p[0]=$file_id;
	runFunction($token,'doDeleteFile',@p);

	return 0;
}

# -------------------------------------------------------------------------------------------------------------

sub smestorage_rmdir {
  my $dir = shift;
	my $dir_id = shift;
	if( $DEBUG>4 ){	print "_rmdir($dir)\n";	}

	if(!defined $dir_id || $dir_id<=0){
		$dir=substr($dir, 0, length($dir)-1) if(substr($dir, length($dir)) eq '/');
		$dir_id = getFileId($dir);

		return -EIO() if(!defined $dir_id || $dir_id<1);
	
		my @files;
		my $i=0;
		while(defined $filelist[$i][1]){
			if($filelist[$i][1]==$dir_id){
				smestorage_unlink($dir) if($filelist[$i][7]==0);						# file type   0 -file; 1- folder
				splice (@filelist, $i, 1);
				last;
			}
			$i++;
		}
	}

	my @p;
	$p[0]=$dir_id;
	runFunction($token,'doDeleteFolder',@p);

	return 0;
}

# -------------------------------------------------------------------------------------------------------------

sub smestorage_mkdir {
	my $dir=shift;
	my $_pid=shift;
	my $structType=shift;
	my $descr=shift;

	my $dir_name;
	my $parent_dir;
	my $parent_id;

	if( $DEBUG>4 ){	print "_mkdir($dir)\n";	}

	$dir=substr($dir, 0, length($dir)-1) if(substr($dir, length($dir)) eq '/');
	$dir_name=substr($dir, rindex($dir, '/')+1);

	if(defined $_pid && $_pid>0 && defined $structType && defined $descr && $needSync==1){
		$parent_id=$_pid;
	}else{
		$parent_dir = substr($dir, 0, rindex($dir, '/')+1);
		$parent_id = getFileId($parent_dir);
	}

	$structType="g" if((!defined $structType || length($structType)<1) && $needSync==1 );
	$descr="" 			if((!defined $structType || !defined $descr || length($descr)<1) && $needSync==1);

	if(!defined $parent_id || $parent_id<0){
		return;
	}

	my @p;
	$p[0]=$dir_name;
	$p[1]=$descr;
	$p[2]=$parent_id;
	$p[3]=$structType;
	$p[4]='0';

	my $result = runFunction($token,'doCreateNewFolder',@p);

	if(!defined $result->{status}){
		if( $DEBUG>0 ){		print "Error. Server not return \"status\"\n";	}
		exit;
	}

	if($result->{status} ne 'ok' || !defined $result->{file}->{fi_name}){
		if( $DEBUG>4 ){		print "Error. Folder not created\n";	}
		return -2;
	}

	my $i=0;
	$i=$#filelist if(defined $filelist[0][0]);

	$filelist[$i][0]=$result->{file}->{fi_name};			# name
	$filelist[$i][1]=$result->{file}->{fi_id};				# id
	$filelist[$i][2]=$result->{file}->{fi_pid};				# parent_id
	
	$filelist[$i][3]=dateToUnix($result->{file}->{fi_created});		# fi_created
	$filelist[$i][4]=dateToUnix($result->{file}->{fi_lastaccessed});	# fi_lastaccessed
	$filelist[$i][5]=dateToUnix($result->{file}->{fi_modified});		# fi_modified
	$filelist[$i][6]=$result->{file}->{fi_size};				# fi_size
	$filelist[$i][7]=$result->{file}->{fi_type};				# fi_type   0 -file; 1- folder
	
#	$filelist[$i][8]=$result->{file}->{fi_description};			# fi_description
#	$filelist[$i][9]=$result->{file}->{fi_tags};				# fi_tags
	if( (defined $result->{file}->{fi_description}) && (index($result->{file}->{fi_description}, "HASH(0x")!=0) ){
		$filelist[$i][8]=$result->{file}->{fi_description};		# fi_description
	}else{
		$filelist[$i][8]="";
	}

	if(defined $result->{file}->{fi_tags} && index($result->{file}->{fi_tags}, "HASH(0x")!=0){
		$filelist[$i][9]=$result->{file}->{fi_tags};		# fi_tags
	}else{
		$filelist[$i][9]="";
	}

	if(defined $result->{file}->{fi_localtime}){
		$filelist[$i][15]=dateToUnix($result->{file}->{fi_localtime});		# fi_localtime
	}else{
		$filelist[$i][15]=1;
	}

	$filelist[$i][15]=$filelist[$i][5] if($filelist[$i][15]<2 || $filelist[$i][15]==946677600);

	$filelist[$i][10] ='';			# content
	$filelist[$i][11] ='';			# cache
	$filelist[$i][12] ='';			# last update
	$filelist[$i][13] ='';			# shadow file
	$filelist[$i][17] =0;				# is empty file

	return 0;
}

# -------------------------------------------------------------------------------------------------------------

sub smestorage_write {
	my $file = shift;
	my $buffer = shift;
	my $offset = shift;
	if( $DEBUG>4 ){	print "_write($file, '". substr($buffer,0,150) ."', $offset)\n";	}

	my $file_name = substr($file, rindex($file, '/')+1);
	my $parent_folder = substr($file, 0, rindex($file, '/'));

	my $file_id = getFileId($file, 0, 1);
	my $parent_id = getFileId($parent_folder);

	return -EIO() if(!defined $parent_id);

	my $data;
=head1
	if($offset==0){
		$data=$buffer;
	}else{
		$data=$buffer;
	}
=cut
	$data=$buffer;

	my $L=0;
	if(!defined $file_id || $file_id<=1){			# file is shadow ?
		my $i=0;
		while( defined $filelist[$i][1] ){
			if(    $filelist[$i][1] == 1
					&& $filelist[$i][0] eq $file_name
					&& $filelist[$i][2] == $parent_id
					&& $filelist[$i][13] eq '1'
			){
				$filelist[$i][10]=$filelist[$i][10] . $data;
				$filelist[$i][17]=0;				# is empty file
				$L=1;
				last;
			}
			$i++;
		}
	}else{
		my $i=0;
		while( defined $filelist[$i][1] ){
=head1
			if(    $filelist[$i][1] == $file_id
					&& $filelist[$i][0] eq $file_name
					&& $filelist[$i][2] == $parent_id
			){
=cut
			if($filelist[$i][1]==$file_id){
				$filelist[$i][10]=$filelist[$i][10] . $data;
				$filelist[$i][17]=0;				# is empty file
				$L=1;
				last;
			}
			$i++;
		}
	}

	if($L){
		if($DEBUG>4){	print "_write => data is added\n";	}
	}else{
		if($DEBUG>4){	print "_write => data is not added\n";	}
	}

	return length($buffer);
}

# -------------------------------------------------------------------------------------------------------------

sub smestorage_flush {
	my $file=shift;
	my $path=shift;
	my $ulocaltime=shift;
	my $pid=shift;
	my $id=shift;
#	utf8::encode($file);

	if( $DEBUG>4 ){	print "_flush($file)\n";	}

	my $n;
	my $file_name;
	my $parent_folder;
	my $parent_id;
	my $data;
	my @p;

	if(!defined $path || !defined $ulocaltime || !defined $pid){
		$file_name=substr($file, rindex($file, '/')+1);
		$parent_folder=substr($file, 0, rindex($file, '/'));

#		$file_id = getFileId($file);
		$parent_id = getFileId($parent_folder);

		if(!defined $parent_id || $parent_id<0){
			if( $DEBUG>4 ){		print "Error. File not found\n";	}
			return -EIO();
		}

		my $k=0;
		while(defined $filelist[$k][0]){
			if($filelist[$k][0] eq $file_name && $parent_id==$filelist[$k][2]){
				$n=$k;
				last;
			}
			$k++;
		}
		
		if(defined $n){
			$data = $filelist[$n][10];
		}else{
			my $id2=getFileId($file);
			if(!defined $id2 || $id2<0){
				if( $DEBUG>4 ){		print "Error 2. File not found\n";	}
				return -EIO();
			}

			$k=0;
			while (defined $filelist[$k][1]){
				if($id2==$filelist[$k][1]){
					$n=$k;
					last;
				}
				$k++;
			}
			$data = $filelist[$n][10] if(defined $n);
		}		

		$data=" " if(defined $data && length($data)<1 && defined $filelist[$n][17] && $filelist[$n][17]==1);
		if(!defined $data || length($data)<1){
			if($DEBUG>4){		print "Warning. Not defined data\n";	}
			return 0;
		}

		if(length($data)<$SMALL_FILE_SIZE){	
			return quickUpload($filelist[$n][0], $data, '', '', $filelist[$n][2], $filelist[$n][1], $n);
		}

		$p[0]=$filelist[$n][0];		# fi_name
#print "filelist[n][0] = ".$filelist[$n][0].";\n";
#print "p[0] = ".$p[0].";\n";
		$p[1]=$filelist[$n][8];		# fi_description
		$p[2]=$filelist[$n][9];		# fi_tags
		$p[3]=$filelist[$n][2];		# fi_pid
		$p[4]=$filelist[$n][0];		# fi_filename
		$p[5]='xml';							# responsetype
		$p[6]='';									# responsedata
		$p[7]='';									# encryptphrase
		$p[8]='';									# Reserved
		$p[9]='g';								# fi_structtype
		$p[10]=$filelist[$n][1];	# fi_id
	#	$p[9]='';									# fi_id
		$p[11]='n';								# chunkifbig
		$p[12]='';								# File local time

		$p[10]='' if($p[10]==1);

		my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
		$mon =$mon + 1;
		$year=$year + 1900;
	
		$mon ="0". $mon  if($mon <10);
		$mday="0". $mday if($mday<10);
		$hour="0". $hour if($hour<10);
		$min ="0". $min  if($min <10);
		$sec ="0". $sec  if($sec <10);

		$p[12]="$year-$mon-$mday $hour:$min:$sec";
	
		return -EIO() if(!defined $p[0]);
	}else{
		$id='' if(!defined $id || length($id)<1 || $id<1);

		my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($ulocaltime);
		$mon =$mon + 1;
		$year=$year + 1900;

		$mon ="0". $mon  if($mon <10);
		$mday="0". $mday if($mday<10);
		$hour="0". $hour if($hour<10);
		$min ="0". $min  if($min <10);
		$sec ="0". $sec  if($sec <10);

		if($SMALL_FILE_SIZE>(-s $path)){	
			return quickUpload($file, '', $path, "$year-$mon-$mday $hour:$min:$sec", $pid, $id);
		}

		$p[0]=$file;							# fi_name
		$p[1]="";									# fi_description
		$p[2]="";									# fi_tags
		$p[3]=$pid;								# fi_pid
		$p[4]=$path;							# fi_filename
		$p[5]='xml';							# responsetype
		$p[6]='';									# responsedata
		$p[7]='';									# encryptphrase
		$p[8]='';									# Reserved
		$p[9]='g';								# fi_structtype
		$p[10]=$id;								# fi_id
	#	$p[9]='';									# fi_id
		$p[11]='n';								# chunkifbig
		$p[12]="$year-$mon-$mday $hour:$min:$sec";				# File local time
		$n=0;
	}
#print Dumper(@p)."\n";
	my $result = runFunction($token, 'doInitUpload', @p);

	if(!defined $result->{uploadcode}){
		if($DEBUG>4){		print "Error. Uploadcode is not get \n";	}
		return -EIO();
	}

	if(upload_File($result->{uploadcode}, $p[0], $data, $path)!=0){
		if( $DEBUG>4 ){		print "upload_File() ==> -1\n";	}
		$filelist[$n][10] = '';				# content
		return -EIO();
	}

	my @p2;
	$p2[0]=$result->{uploadcode};			# uploadcode
	
	$result = runFunction($token, 'doCompleteUpload', @p2);

	if(defined $result->{status} && $result->{status} eq "ok"){		# Add info to cache
		$filelist[$n][0]=$result->{file}->{fi_name};						# name
		$filelist[$n][1]=$result->{file}->{fi_id};							# id
		$filelist[$n][2]=$result->{file}->{fi_pid};							# parent_id

		$filelist[$n][3]=dateToUnix($result->{file}->{fi_created});					# fi_created
		$filelist[$n][4]=dateToUnix($result->{file}->{fi_lastaccessed});		# fi_lastaccessed
		$filelist[$n][5]=dateToUnix($result->{file}->{fi_modified});				# fi_modified
		$filelist[$n][6]=$result->{file}->{fi_size};												# fi_size
		$filelist[$n][7]=$result->{file}->{fi_type};												# fi_type   0 -file; 1- folder

#		$filelist[$n][8]=$result->{file}->{fi_description};									# fi_description
#		$filelist[$n][9]=$result->{file}->{fi_tags};												# fi_tags

		if(defined $result->{file}->{fi_description} && index($result->{file}->{fi_description}, "HASH(0x")!=0){
			$filelist[$n][8]=$result->{file}->{fi_description};		# fi_description
		}else{
			$filelist[$n][8]="";
		}

		if(defined $result->{file}->{fi_tags} && index($result->{file}->{fi_tags}, "HASH(0x")!=0){
			$filelist[$n][9]=$result->{file}->{fi_tags};		# fi_tags
		}else{
			$filelist[$n][9]="";
		}

		if(defined $result->{file}->{fi_localtime}){
#print $filelist[$n][15];
			$filelist[$n][15]=dateToUnix($result->{file}->{fi_localtime});		# fi_localtime
		}else{
			$filelist[$n][15]=1;
		}

		$filelist[$n][15]=$filelist[$n][5] if($filelist[$n][15]<2 || $filelist[$n][15]==946677600);

		$filelist[$n][11]=$filelist[$n][10];		# cache
		$filelist[$n][10]='';										# content
		$filelist[$n][13]='';										# shadow file
		$filelist[$n][14]=$data;
		$filelist[$n][17]=0;										# is empty file
		if($DEBUG>4){		print "Upload => success \n";	}
		return 0;
	}else{
		if($DEBUG>4){		print "Upload => fail \n";	}
		return -EIO();
	}
}

# -------------------------------------------------------------------------------------------------------------
sub quickUpload {
	my $filename=shift;
	my $file=shift;
	my $path=shift;
	my $ulocaltime=shift;
	my $pid=shift;
	my $id=shift;
	my $num=shift;

	if( $DEBUG>4 ){	print "_quickUpload($filename, '". substr($file,0,150) ."', $path, $ulocaltime, $pid, $id)\n";	}

	$file="" if(!defined $file);
	$num=0 if(!defined $num);

	my $fileSize=length($file);
	if(defined $path && length($path)>0){
		return -EIO() if(!(-e $path));
		$fileSize=-s($path);		#;
		if($fileSize==0){
			$fileSize=1;
			$file=" ";
			$path="";
		}
	}

	my $socket = new IO::Socket::INET(		#open socket to the server
		PeerAddr => $server_sme,	#this is development server
		PeerPort => 80,			#standart http port
		Proto    => 'tcp');			
	if(!$socket){
		if( $DEBUG>4 ){	print "no socket :$!";	}
		return -EIO();
	}

	#create boundary for multypart form
	my $boundry="--29cabc5a6d14420cb917e3c0ecfe11ac";

	my $footer="\r\n--$boundry--\r\n";			# create footer of POST request body
	my $query='';
	$query.="--$boundry\r\n";
	$query.="Content-Disposition: form-data; name=\"fi_pid\"\r\n";
	$query.="\r\n";
	$query.="$pid\r\n";
	$query.="--$boundry\r\n";
	$query.="Content-Disposition: form-data; name=\"file_name1\"\r\n";
	$query.="\r\n";
	$query.="$filename\r\n";
	$query.="--$boundry\r\n";
	$query.="Content-Disposition: form-data; name=\"file_desc1\"\r\n";
	$query.="\r\n";
	$query.="\r\n";
	$query.="--$boundry\r\n";
	$query.="Content-Disposition: form-data; name=\"file_tags1\"\r\n";
	$query.="\r\n";
	$query.="\r\n";
	$query.="--$boundry\r\n";
	$query.="Content-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n";
	$query.="\r\n";
	$query.="10000000\r\n";
	$query.="--$boundry\r\n";
	$query.="Content-Disposition: form-data; name=\"file_1\"; filename=\"$filename\"\r\n";
	$query.="Content-Type: text/html\r\n";
	$query.="\r\n";

	my $content_length=$fileSize+length($query)+length($footer);		# calculate content length. content length is file size + size of header and footer
	my $header="";
	$header.="POST /api/". $token ."/doUploadFiles/ HTTP/1.1\r\n";
	$header.="Host: $server_sme\r\n";
	$header.="Content-Type: multipart/form-data; boundary=$boundry\r\n";
	$header.="User-Agent: FuseSMEclient\r\n";
	$header.="Content-Length: $content_length\r\n";
	$header.="\r\n";

	my $fileData;
	if(defined $path && length($path)>0){
		if(-e($path)){
			return -EIO() if(!open($fileData, "< ".$path));
		}else{
			return -EIO();
		}
	}

	print $socket $header;		#print body header
	print $socket $query;			#print request to the server
	
	if($file ne ""){
		print $socket $file;			#print request to the server
	}else{
		my $L=0;
		my $buffer="";		#this is data buffer
		my $posted=0;			#this is variable to calculate amount of posted bytes
		my $i=0;
		my $cp=1024*2;

		my $lt=0;
		my $nt=0;
		while($i<$fileSize){
			$cp=$fileSize-$i if($cp>$fileSize-$i);

			if($cp==0){	# file is send
				if( $DEBUG>4 ){		print "file is send\n";	}
				last;
			}

			read($fileData, $buffer, $cp);

			print $socket $buffer;		#post data to the server
			$posted+=length($buffer);	#calculate count of sent bytes

			$nt=time();								# for sync
			if($nt-$lt>0){
				$|=1;
#				print "[".$posted."]\n" if($needSync==1);
				sendSyncResponse("[".$posted."]\n");
				$|=0;
				$lt=$nt;
			}

			$i=$i+length($buffer);
		}# while

		close $fileData if($fileData);

		$|=1;
		print "[". $posted ."]\n" if($needSync==1);		# for sync
		sendSyncResponse("[". $posted ."]\n");
		$|=0;
	}
	print $socket $footer;	# print request to the server

	my $rx='';
	while(<$socket>){				# read server response
		$rx.=$_;
		$rx=~ s/\r//g;
		return -EIO() if(index($rx, ' 200 OK')==-1);
		last if(index($rx, "</file>")>-1 || index($rx, "<spaceinfo>")>-1 || index($rx, "</providers>")>-1 || index($rx, "<total>")>-1 || index($rx, "</response")>-1);
	}
#print "rx=$rx\n";

	my $fi_id='';						# get file id
	my $s1=index($rx, '<fi_id');
	my $s2=index($rx, '</fi_id');
	$fi_id=substr($rx, $s1+length('<fi_id'), $s2-$s1-length('<fi_id')) if($s1>-1 && $s2>-1);
	if($fi_id eq ''){
		$s1=index($rx, 'fi_id');
		$s2=index($rx, '/fi_id');
		$fi_id=substr($rx, $s1+length('fi_id'), $s2-$s1-length('fi_id')) if($s1>-1 && $s2>-1);
	}

	if((!defined $fi_id || $fi_id eq '') && index($rx, 'login_token_expired')>0){
		$token='*';
		signIn();
		return quickUpload($filename, $file, $path, $ulocaltime, $pid, $id, $num);
	}

#print "fi_id=$fi_id\n";
	return -EIO() if($fi_id eq '');
	my $c=0;
	while(index($fi_id, "\n")>-1 && $c<10){
		$s1=index($fi_id, "\n");
		$s2=index($fi_id, "\n", $s1+1);
		last if($s1==-1 || $s2==-1);
		if($s2+1<length($fi_id)){
			$fi_id=substr($fi_id, 0, $s1) . substr($fi_id, $s2+1);
		}else{
			$fi_id=substr($fi_id, 0, $s1);
		}
		$c++;
	}
	return -EIO() if($fi_id eq '');
#print "fi_id=$fi_id\n";
	$fi_id=~ s/[>< A-z\n\r]//g;
#print "fi_id=$fi_id\n";

	if(defined $ulocaltime && length($ulocaltime)>0){
		my $mStr="fi_localtime=".$ulocaltime;
		my @p;
		$p[0]=$fi_id;
		$p[1]=$mStr;
		$p[2]='n';

		my $result = runFunction($token,'doModifyFile',@p);
		return -EIO() if(!defined $result->{status} && $result->{status} ne "ok");
	}

	$filelist[$num][0]=$filename;	# name
	$filelist[$num][1]=$fi_id;		# id
	$filelist[$num][2]=$pid;			# parent_id

	$filelist[$num][3]=(defined $ulocaltime && length($ulocaltime)>0)?(dateToUnix($ulocaltime)):(time());		# fi_created
	$filelist[$num][4]=(defined $ulocaltime && length($ulocaltime)>0)?(dateToUnix($ulocaltime)):(time());		# fi_lastaccessed
	$filelist[$num][5]=(defined $ulocaltime && length($ulocaltime)>0)?(dateToUnix($ulocaltime)):(time());		# fi_modified
	$filelist[$num][6]=$fileSize;	# fi_size
	$filelist[$num][7]=0;					# fi_type   0 -file; 1- folder

	$filelist[$num][8]="";			# fi_description
	$filelist[$num][9]="";			# fi_tags

	if(defined $ulocaltime && length($ulocaltime)>0){
		$filelist[$num][15]=dateToUnix($ulocaltime);		# fi_localtime
	}else{
		$filelist[$num][15]=1;
	}

	$filelist[$num][15]=$filelist[$num][5] if($filelist[$num][15]<2 || $filelist[$num][15]==946677600);

	$filelist[$num][10]='';			# content
	$filelist[$num][11]=$file;	# cache
	$filelist[$num][12]='';			# last update
	$filelist[$num][13]='';			# shadow file
	$filelist[$num][14]=$file;
	$filelist[$num][17]=0;			# is empty file
	if($DEBUG>4){		print "Upload => success \n";	}
	return 0;
}

# -------------------------------------------------------------------------------------------------------------

sub upload_File {
	my $uploadcode1 = shift;
	my $filename1 = shift;
	my $file1 = shift;
	my $path = shift;

	$file1="" if(!defined $file1);
	if( $DEBUG>4 ){	print "_upload_File($uploadcode1, $filename1, '". substr($file1,0,150) ."')\n";	}

	my $startbyte=0; 	#this variable contains file byte position to start uploading.
				# for the first uploading it is 0 (this is simple uploading)

#	&Post1($uploadcode1,$filename1,$file1,$startbyte);		#call function to upload file. Uploading should be paused on this call

	if( &Post1($uploadcode1, $filename1, $file1, $startbyte, $path)!=0  ){
		return -1;
	}

	return 0;
}

################  this is function that process uploading. ###################

sub Post1{
	my $uploadcode=shift;
	my $filename=shift;
	my $file=shift;
	my $startbyte=shift;
	my $path=shift;
	if( $DEBUG>4 ){	print "_Post1($uploadcode, $filename, '". substr($file,0,150) ."', $startbyte)\n";	}

	if( !defined $file ){
		$file="";
	}

	my $fileSize=length($file);
	if( defined $path && length($path)>0 ){
		if( !(-e $path) ){
			return -9;
		}
		my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$fsize,$atime,$mtime,$ctime,$blksize,$blocks) = stat($path);
		$fileSize=$fsize;
	}

	if($startbyte>=$fileSize){
		return -1;
	}

	#this function uploads the file
	#input data are taken from global variables
	#the function works in 2 modes: uploading and uploading resume
	#the mode depends from variable $startbyte
	#if $startbyte is 0 then this is simple uploading
	my $socket = new IO::Socket::INET(		#open socket to the server
		PeerAddr => $server_sme,	#this is development server
		PeerPort => 80,			#standart http port
		Proto    => 'tcp');			
	if( !$socket ){
		if( $DEBUG>4 ){	print "no socket :$!";	}
		return -1;
	}

	#create boundary for multypart form
	my $boundry="---------------------------624433355719741605845657464";
	
	#create header of POSt resuest body
	my $header="--$boundry\r\n";
	$header.="Content-Disposition: form-data; name=\"uploadedfile\";";
	$header.=" filename=\"$filename\"\r\n";
	$header.="Content-Type: image/jpeg\r\n";
	$header.="\r\n";
	#create footer of POSt request body
	my $footer="\r\n--$boundry--\r\n";
	#calculate content length
	#content length is file size + size of header and footer
	my $content_length=$fileSize+length($header)+length($footer);
	if($startbyte>0){
		$content_length-=$startbyte;
		#if this is resume then decrease content length for already uploaded bytes
	}

	my $query;
	
	#set up query string
	my $qs=$uploadcode;
	if($startbyte>0){
		#if this is resume of uploading then query string must be extended
		$qs.=',0,'.$startbyte;
	}
	
	#prepare http request
	$query = "POST /cgi-bin/uploader/uploader1.cgi?$qs HTTP/1.1\r\n";
	$query.="Host: $server_sme\r\n";
	$query.="Content-Type: multipart/form-data; boundary=$boundry\r\n";
	$query.="User-Agent: FuseSMEclient\r\n";
	$query.="Content-Length: $content_length\r\n";
	$query.="\r\n";
	
	#print request to the server
	print $socket $query;
	#print body header
	print $socket $header;

	my $UploadStatus=0;
  my $s;
	my $data='';

	pipe(CHILD_RDR,  PARENT_WTR);
	PARENT_WTR->autoflush(1);

	my $fileData;
	if( defined $path && length($path)>0 ){
		if( -e($path) ) {
			if( !open($fileData, "< ".$path) ){
				return -10;			
			}
		}else{
			return -11;
		}
	}

	my $L=0;
	my $buffer;		#this is data buffer
	my $posted=0;		#this is variable to calculate amount of posted bytes
				#it is used to pause uploading in this example
	my $i=$startbyte;
	my $cp=0;

	my $lt=0;
	my $nt=0;
	while( $i<$fileSize ){
		if( $UploadStatus==1 ){
			if( $DEBUG>4 ){		print "UploadStatus==1\n";	}
			return -1;
		}

		$cp=1024*2;
		if( $cp>$fileSize - $i ){
			$cp=$fileSize - $i;
		}

		if($cp==0){	# file is send
			if( $DEBUG>4 ){		print "file is send\n";	}
			last;
		}

		if( defined $path && length($path)>0 ){
			read($fileData, $buffer, $cp);
		}else{
			$buffer=substr($file,$i,$cp);
		}

		print $socket $buffer;		#post data to the server
		$posted+=length($buffer);	#calculate count of sent bytes

		$nt=time();								# for sync
		if($nt-$lt>0){
			$|=1;
			if($needSync==1){
#				print "[". $posted ."]\n";
				sendSyncResponse("[". $posted ."]\n");
			}
			$|=0;
			$lt=$nt;
		}

		if( ($i==0) && ($fileSize>1024*50) && ($L==0) ){
			$L=1;
			my $fork1=fork;
			if( $fork1 == 0 ) {
				close CHILD_RDR;
				sleep(4);

				#execute function  getUploadStatusOnFail to get status of uploading.
				my @p2;
				$p2[0]=$uploadcode;			# uploadcode
				my $r = runFunction($token, 'getUploadStatusOnFail', @p2);

				if(  (!defined $r) || (!defined $r->{status}) || !( $r->{status} eq "ok")){
					if( $DEBUG>0 ){		print "getUploadStatusOnFail => status not 'ok'\n";	}
					print PARENT_WTR "1\n";
					close PARENT_WTR;
					exit(0);
				}
				#$startbyte=$r->{uploaded}; 	#get count of saved bytes. 
				print PARENT_WTR "0\n";
				close PARENT_WTR;

				exit(0);
			}else{
				close PARENT_WTR;
				$s=IO::Select->new;
				$s->add(\*CHILD_RDR);

			}
		}

		if( $L==1 ){
			if( $s->can_read(0) ){
				$data='';
				$data = <CHILD_RDR>;
				if( defined $data ){
					chomp($data);
					$UploadStatus = $data;
				}
			}
		}

		$i=$i+length($buffer);
	}

	if($needSync==1){
		close $fileData;
	}

	close CHILD_RDR;
	$|=1;
	if($needSync==1){
		print "[". $posted ."]\n";   # for sync
		sendSyncResponse("[". $posted ."]\n");
	}
	$|=0;

	#after file body is sent, then post body footer
	print $socket $footer;

	#read server response
	$|=0;
	my $r = '';
	while(<$socket>) {
		$r .= $_;
		last;
	}

	close $socket;
#	print $r;
	return 0;
}

# -------------------------------------------------------------------------------------------------------------

sub smestorage_chown{

  return 0;
}

# -------------------------------------------------------------------------------------------------------------

sub smestorage_mknod{
	my $file = shift;
	my $mode = shift;
	my $device = shift;
	if( $DEBUG>4 ){	print "_mknod($file, $mode, $device)\n";	}

	my $file_name = substr($file, rindex($file, '/')+1);
	my $parent_dir = substr($file, 0, rindex($file, '/')+1);

	my $parent_id = getFileId($parent_dir);

	if(!defined $parent_id || $parent_id<0){
		return;
	}

	my $ax1 = $#filelist;
	while(defined $filelist[$ax1][1]){
		$ax1++;
	}

	my $i=0;
	while( defined $filelist[$i][1] ){
		if(    ($filelist[$i][1] == 1 )
				&& ($filelist[$i][0] eq $file_name)
				&& ($filelist[$i][2] == $parent_id)
				&& ($filelist[$i][13]eq '1')
		){
			$ax1=$i;
			last;
		}
		$i++;
	}

	my $time=time();
	$filelist[$ax1][0] 	=$file_name;	# fi_name
	$filelist[$ax1][1] 	='1';					# fi_id
	$filelist[$ax1][2] 	=$parent_id;	# parent_id
	$filelist[$ax1][3] 	=$time;				# fi_created
	$filelist[$ax1][4] 	=$time;				# fi_lastaccessed
	$filelist[$ax1][5] 	=$time;				# fi_modified
	$filelist[$ax1][6] 	=0;						# fi_size
	$filelist[$ax1][7] 	=0;						# fi_type   0 -file; 1- folder
	$filelist[$ax1][8] 	='';					# fi_description
	$filelist[$ax1][9] 	='';					# fi_tags
	$filelist[$ax1][10] ='';					# content
	$filelist[$ax1][11] ='';					# cach
	$filelist[$ax1][12] ='';					# 
	$filelist[$ax1][13] ='1';					# shadow file
	$filelist[$ax1][17] =1;						# is empty file
#	smestorage_flush($file);

  return 0;
}
#-------------------------------------------------------------------------------------------------------------
sub smestorage_truncate{
	my $file = shift;
	my $offset = shift;

	if( $DEBUG>4 ){	print "_truncate($file, $offset)\n";	}
	if(defined $offset && $offset==0){
		my $file_name =substr($file, rindex($file, '/')+1);
		my $parent_dir=substr($file, 0, rindex($file, '/')+1);
		my $parent_id =getFileId($parent_dir);
		if(defined $parent_id && $parent_id>=0){
			my $i=0;
			while(defined $filelist[$i][1]){
				if($filelist[$i][0] eq $file_name && $filelist[$i][2] == $parent_id){
					$filelist[$i][17]=1;						# is empty file
					last;
				}
				$i++;
			}# while
		}# if
	}# if

	return 0;
}
#-------------------------------------------------------------------------------------------------------------
sub smestorage_statfs {
	if( $DEBUG>4 ){	print "_statfs()\n";	}

	my $namelen = 1024;
	my $files = 100000000;
	my $files_free = 100000000;
	my $blocks = 1073741824;
	my $blocks_free = 1073741824;
	my $blocks_size = 1024;

	return ($namelen, $files, $files_free, $blocks, $blocks_free, $blocks_size);
}
#-------------------------------------------------------------------------------------------------------------
sub smestorage_symlink {
	if( $DEBUG>4 ){	print "_symlink()\n";	}

	return 0;
}
#-------------------------------------------------------------------------------------------------------------
sub smestorage_link {
	if( $DEBUG>4 ){	print "_link()\n";	}

	return 0;
}
#-------------------------------------------------------------------------------------------------------------


##############################################################################################################
# run fuse ###################################################################################################
##############################################################################################################

my @extraargs;

# fork and exit parent process to put FuseSme into background
if( $DEBUG>0 ){		print "Backgrounding...\n";	}
fork() and exit(0);
if(  $allow_other==1  ){
  Fuse::main(mountpoint => $mountpoint,
      getdir => \&smestorage_getdir,
      getattr => \&smestorage_getattr,
      read => \&smestorage_read,
      rename => \&smestorage_rename,
      unlink => \&smestorage_unlink,
      rmdir => \&smestorage_rmdir,
      mkdir => \&smestorage_mkdir,
      write => \&smestorage_write,
      flush => \&smestorage_flush,
      chown => \&smestorage_chown,
      chmod => \&smestorage_chown,
      mknod => \&smestorage_mknod,
      truncate => \&smestorage_truncate,
      statfs => \&smestorage_statfs,

      readlink => \&smestorage_readlink,
      symlink => \&smestorage_symlink,
      link => \&smestorage_link,
 
      mountopts => "allow_other",
      @extraargs,
  );
}else{
  Fuse::main(mountpoint => $mountpoint,
      getdir => \&smestorage_getdir,
      getattr => \&smestorage_getattr,
      read => \&smestorage_read,
      rename => \&smestorage_rename,
      unlink => \&smestorage_unlink,
      rmdir => \&smestorage_rmdir,
      mkdir => \&smestorage_mkdir,
      write => \&smestorage_write,
      flush => \&smestorage_flush,
      chown => \&smestorage_chown,
      chmod => \&smestorage_chown,
      mknod => \&smestorage_mknod,
      truncate => \&smestorage_truncate,
      statfs => \&smestorage_statfs,

      readlink => \&smestorage_readlink,
      symlink => \&smestorage_symlink,
      link => \&smestorage_link,
 
      @extraargs,
  );
}

exit;

__DATA__
Usage: smestorage mountpoint user:password [providerUser:providerPassword] [--server=host] [--ao] [--DEBUG]

Example:
smestorage /folder1 login1:password1 --server=eu.smestorage.com --ao

