<?
include("init.php");
set_time_limit( (($config['settings']['scanner_timeout']*$config['settings']['limit_scanner'])+120) );

$LCKFile = $config['root']."/cron_file.lck";
$_time = time();

if (file_exists($LCKFile)){
	$time=filectime($LCKFile);
	if ((time()-$time)>60*120){
		unlink($LCKFile);
	}
	die('lck');
}
_log($LCKFile,"Запуск");

function ShutDownAuto(){
	global $LCKFile;
	if (file_exists($LCKFile)){
		if (unlink($LCKFile)){
			echo "Unlink!";
		}
		Serval::call('db')->Dissconnect();
	}
}

register_shutdown_function('ShutDownAuto');



/* Создаем задание */ 
if( $get = Serval::call('db')->Select("mod_projects",
	"id",
	"((status='ok' AND (
		UNIX_TIMESTAMP( last_date_check + INTERVAL `check_hour` HOUR )<={$_time} 
		OR (IF(UNIX_TIMESTAMP( last_date_check )=0,IF(UNIX_TIMESTAMP( DATE + INTERVAL 10 MINUTE )<={$_time},1,0),0))=1
		OR (IF(mod_projects.attempt_check>0 && UNIX_TIMESTAMP( last_date_check + INTERVAL 10 MINUTE )<={$_time},1,0))=1
	)) OR status='check') AND is_active='yes'") ){

	$i = 1;
	foreach($get as $val){
		Serval::call('db')->run("DELETE FROM mod_scan_log WHERE scan_id IN(
			SELECT id FROM mod_scan WHERE status IN('new', 'process') AND project_id= '{$val['id']}'
		) AND project_id= '{$val['id']}'");
		Serval::call('db')->run("SELECT id FROM mod_scan WHERE status IN('new', 'process') AND project_id= '{$val['id']}'");
		
		Serval::call('db')->insert("mod_scan",array('id'=>(time()+$i),'project_id'=>$val['id'],'status'=>'new','date'=>'SQL_TIME_NOW()'));
		Serval::call('db')->update("mod_projects",array('status'=>'waiting','attempt_check'=>0),"id=".$val['id']);
		$i++;
	}
	
}


 
$limit = $config['settings']['limit_scanner']>0 ? $config['settings']['limit_scanner'] : 5;
$messageData = array();

if($scan = Serval::call('db')->Select("mod_scan
		INNER JOIN mod_projects ON(mod_scan.project_id=mod_projects.id)
		LEFT JOIN mod_users ON(mod_users.id=mod_projects.users_id)
	",
	"mod_scan.id AS scanId, 
		mod_scan.status AS scanStatus, 
		mod_scan.data_process AS scanData, 
		mod_scan.step AS scanStep, 
		mod_scan.file_c AS scanFile_c,
		mod_scan.dir_c AS scanDir_c, 
		mod_users.email AS userEmail, 
		mod_projects.*",
	"mod_scan.status IN('new','process')",
	"ORDER BY mod_scan.date ASC LIMIT {$limit}") ){
	
	foreach($scan as $val){
		$dirMax = ($val['maxdir']>0 ? $val['maxdir'] : ($config['settings']['scanner_maxdir']>0 ? $config['settings']['scanner_maxdir'] : 100));
		_log($LCKFile,"ID: ".$val['id']." x ".$val['scanId']." (maxdir: {$dirMax} crypt_type: {$val['crypt_type']})");

		if($val['scanStatus']=="new"){
			Serval::call('db')->update("mod_scan",array('status'=>'process'),"id={$val['scanId']}");
			Serval::call('db')->update("mod_projects",array('status'=>'process','last_date_process'=>'SQL_TIME_NOW()'),"id=".$val['id']);
		}else{
			Serval::call('db')->update("mod_projects",array('last_date_process'=>'SQL_TIME_NOW()'),"id=".$val['id']);
		}
		
		Serval::call('db')->Dissconnect();
		$res = Serval::call('Check')->run(array(
			'scode'=>md5($val['scode'].$config['solt']),
			'dirMax'=>$dirMax,
			'crypt_type'=>$val['crypt_type'],
			'protocol'=>$val['protocol'],
			'url'=>$val['domain'],
			'script'=>$val['path'],
			'rootDir'=>$val['spath'],
			'listDir'=>(!empty($val['scanData']) ? unserialize($val['scanData']) : array()),
			'ext'=>array_map("trim",explode(",",$val['ext'])),
			'exc'=>array_map("trim",explode(",",$val['exc'])),
			'excf'=>array_map("trim",explode(",",$val['excf'])),
			'maxtime'=>$val['maxtime'],
			'timeout'=>($config['settings']['scanner_timeout']>0 ? $config['settings']['scanner_timeout'] : 30)
		));
		Serval::call('db')->connect($config);
		$update = array();


		if($res['status']=="ok"){
			$file_c = (is_array($res['log']['file']) && count($res['log']['file'])>0) ? count($res['log']['file']) : 0;
			$dir_c 	= (is_array($res['log']['scan']) && count($res['log']['scan'])>0) ? count($res['log']['scan']) : 0;
			$update = array(
				'step'=>($val['scanStep']+1),
				'file_c'=>($val['scanFile_c']+$file_c),
				'dir_c'=>($val['scanDir_c']+$dir_c),
			);
			if(is_array($res['log']['dir']) && count($res['log']['dir'])>0){
				$update['data_process'] = serialize($res['log']['dir']);
				$update['status'] = 'process';	
			}else{
				$update['data_process'] = '';
				$update['status'] = 'ok';
			}
			$sql = array();
			if(is_array($res['log']['file']) && count($res['log']['file'])>0){
				foreach($res['log']['file'] as $v){
					$dir 		= $v['level']==0 ? '/' : trim($res['log']['scan'][$v['dirId']]['path']);
					$sql[] 	= "('{$val['scanId']}','{$val['id']}',".Serval::call('db')->quote($dir.trim($v['name'])).",".Serval::call('db')->quote($dir).",".Serval::call('db')->quote($v['size']).",".Serval::call('db')->quote($v['hash']).",".Serval::call('db')->quote(md5($dir.trim($v['name']))).",".Serval::call('db')->quote(trim($v['level'])).",'new')";
				}
				$sql = array_chunk($sql,1000);
				foreach($sql as $v){
					Serval::call('db')->run("INSERT IGNORE mod_scan_log (scan_id,project_id,path,dir,size,is_hash,is_hash_name,level,status) VALUES ".implode(",",$v).";");
				}
				unset($sql);
			}
			$sql = array();
			if(is_array($res['log']['scan']) && count($res['log']['scan'])>0){
				foreach($res['log']['scan'] as $v){
					if(isset($v['continue'])){
						continue;
					}
					$sql[] = "('{$val['scanId']}','{$val['id']}',".Serval::call('db')->quote(trim($v['path'])).",".Serval::call('db')->quote(trim($v['level'])).")";
				}
				$sql = array_chunk($sql,1000);
				foreach($sql as $v){
					Serval::call('db')->run("INSERT IGNORE mod_scan_dir (scan_id,project_id,path,level) VALUES ".implode(",",$v).";");
				}
				unset($sql);
			}
		}else{
			$update['status'] = 'error';
			$update['error_msg'] = $res['msg'];
		}

		if($update['status']=="ok"){
			$val['attempt_check'] = 0;
			Serval::call('db')->update("mod_projects",array('status'=>'ok','attempt_check'=>0,'last_date_check'=>'SQL_TIME_NOW()'),"id={$val['id']}");
			$update['is_last'] = 'yes';

			if( $log = Serval::call('db')->SelectRecord("mod_scan","id, status","status IN('ok','warning') AND project_id='{$val['id']}' AND id!='{$val['scanId']}'","ORDER BY date DESC") ){

				Serval::call('db')->run("UPDATE mod_scan_log, mod_scan_log AS prevScan
					SET mod_scan_log.status=IF(mod_scan_log.is_hash!=prevScan.is_hash,'change','ok')
					WHERE 
						mod_scan_log.scan_id='{$val['scanId']}' AND mod_scan_log.project_id='{$val['id']}'
						AND prevScan.scan_id='{$log['id']}' AND prevScan.project_id='{$val['id']}'
						AND mod_scan_log.is_hash_name=prevScan.is_hash_name
				");

				Serval::call('db')->run("
				INSERT IGNORE mod_scan_log (scan_id,project_id,path,dir,size,is_hash,is_hash_name,status)                            
					SELECT '{$val['scanId']}' AS scan_id, prevScan.project_id, prevScan.path, prevScan.dir, prevScan.size, prevScan.is_hash, prevScan.is_hash_name, 'delete' AS status FROM mod_scan_log AS prevScan 
						WHERE prevScan.scan_id='{$log['id']}' AND prevScan.project_id='{$val['id']}'
							AND prevScan.is_hash_name NOT IN(
								SELECT mod_scan_log.is_hash_name FROM mod_scan_log
									WHERE mod_scan_log.scan_id='{$val['scanId']}' 
										AND mod_scan_log.project_id='{$val['id']}'
							)	
							AND prevScan.status!='delete'
				");

	    	if($stat = Serval::call('db')->Select("mod_scan_log",
					"COUNT(status) AS c, status",
					"mod_scan_log.scan_id='{$val['scanId']}' AND mod_scan_log.project_id='{$val['id']}'",
					"GROUP BY status	")){
					foreach($stat as $v){
						switch($v['status']){
							case 'new':
							case 'change':
							case 'delete':
								$update[$v['status'].'_c'] = $v['c'];
								if($v['c']>0){
									$update['status'] = 'warning';
									$update['is_last'] = 'yes';
								}
							break;
						}
					}
				}
				if($log['status']=="ok"){
					Serval::call('db')->run("DELETE FROM mod_scan_log WHERE scan_id='{$log['id']}' AND project_id= '{$val['id']}'");
					Serval::call('db')->run("DELETE FROM mod_scan WHERE id='{$log['id']}' AND project_id= '{$val['id']}'");
				}
			}else{
				Serval::call('db')->run("UPDATE mod_scan_log SET status='ok' WHERE scan_id='{$val['scanId']}' AND project_id='{$val['id']}'");
			}
			
		}elseif($update['status']!="process"){
			$val['attempt_check'] = ($val['attempt_check']+1);
			if($val['attempt_check']>2){
				$val['attempt_check'] = 0;
			}
			Serval::call('db')->update("mod_projects",array(
			'status'=>'ok',
			'attempt_check'=>$val['attempt_check'],
			'last_date_check'=>'SQL_TIME_NOW()','error_msg'=>$update['error_msg']),"id={$val['id']}");
		}
    unset($res);

    if($update['is_last']=="yes"){
			Serval::call('db')->update("mod_scan",array('is_last'=>'no'),"project_id={$val['id']} AND is_last='yes'");
			Serval::call('db')->run("DELETE FROM mod_scan_dir WHERE scan_id!='{$val['scanId']}' AND project_id='".(int)$val['id']."'");
			if( $dirs = Serval::call('db')->Select("mod_scan_log",
				"DISTINCT dir",
				"scan_id='{$val['scanId']}' AND project_id='{$val['id']}' AND status!='ok'") ){
				$dirList = array();
				foreach($dirs as $v){
					$str = '/';
					foreach(explode("/",$v['dir']) as $_v){
						if(!empty($_v)){
							$str.=$_v.'/';
							$dirList[$str] = Serval::call('db')->quote($str);
						}
					}
				}
				unset($dirs);
				Serval::call('db')->run("UPDATE mod_scan_dir SET is_change='yes' 
					WHERE path IN(".implode(",",$dirList).") AND scan_id='{$val['scanId']}' AND project_id='{$val['id']}'");
				unset($dirList);
			}
		}
		$sendMsg = true;
		if($val['attempt_check']>0){
			$sendMsg = false;
			Serval::call('db')->run("DELETE FROM mod_scan WHERE id={$val['scanId']}");
			Serval::call('db')->run("DELETE FROM mod_scan_log WHERE scan_id={$val['scanId']}");
			Serval::call('db')->run("DELETE FROM mod_scan_dir WHERE scan_id={$val['scanId']}");
		}

		if($sendMsg){
			Serval::call('db')->update("mod_scan",$update,"id={$val['scanId']}");
			switch($update['status']){
				case 'error':
				case 'warning':   
	
					$message="<p>Проект: {$val['domain']}. </p>
						<p>".($update['status']=="error" ? $update['error_msg'] : 'Зафиксировано изменение целостности файловой структуры проекта. Пожалуйста, не оставляйте это без внимания!')."</p>
					";
					if($val['is_change_email']=="yes"){
						if($lastLogRes = Serval::call('Projects')->getLogInfo($val['id'],$val['scanId'])){
							if(is_array($lastLogRes['list']['new']) && count($lastLogRes['list']['new'])>0){
								$message.= "<br>Были добавлены файлы:<br>";
								foreach($lastLogRes['list']['new'] as $v){
									$message.= "{$v['path']} [{$v['size']}B]<br>";
								}
							}

							if(is_array($lastLogRes['list']['change']) && count($lastLogRes['list']['change'])>0){
								$message.= "<br>Были изменены файлы:<br>";
								foreach($lastLogRes['list']['change'] as $v){
									$message.= "{$v['path']} [{$v['size']}B]<br>";
								}
							 }

							if(is_array($lastLogRes['list']['delete']) && count($lastLogRes['list']['delete'])>0){
								$message.= "<br>Были удалены файлы:<br>";
								foreach($lastLogRes['list']['delete'] as $v){
									$message.= "{$v['path']} [{$v['size']}B]<br>";
								}
							}
						}
						unset($lastLogRes);
					}
					
					if($users = Serval::call('db')->Select(
						"mod_projects2users, mod_users",
						"mod_users.email",
						"mod_projects2users.projects_id='{$val['id']}'
							AND mod_projects2users.users_id=mod_users.id") ){
	
						foreach($users as $_val){
							sendEmail($_val['email'],"SERVAL: ".$val['domain'], $message );
						}
					}
					sendEmail($val['userEmail'],"SERVAL: ".$val['domain'], $message );
				break;
			}
		}
		unset($update);
	}
}


if( $config['settings']['clear_log']>0 ){
	if( $scanId = Serval::call('db')->Select("mod_scan","id","UNIX_TIMESTAMP( date + INTERVAL {$config['settings']['clear_log']} DAY )<={$_time} 
					AND (is_verified='yes' OR (is_verified='no' AND status='ok'))
					AND is_last='no' 
					AND status!='process'","LIMIT 10") ){
		$scanIds = array();
		foreach($scanId as $val){
			$scanIds[] = $val['id'];
		}
		
		Serval::call('db')->run("DELETE FROM mod_scan_log WHERE scan_id IN(".implode(",",$scanIds).")");
		Serval::call('db')->run("DELETE FROM mod_scan WHERE mod_scan.id IN(".implode(",",$scanIds).")");	

		unset($scanIds);
		unset($scanId);
	}		
}

/* Проверка на залипание */
if( $get = Serval::call('db')->Select("mod_projects","id","status='process' AND UNIX_TIMESTAMP( last_date_process + INTERVAL 1 HOUR )<={$_time}") ){
	foreach($get as $val){
		MyCMS::call('db')->update("mod_projects",array(
			'status'=>'check',
			'error_msg'=>'',
			'attempt_check'=>'0'
		),"id='{$val['id']}'");
	}
}

_log($LCKFile,"Завершено");
@copy($LCKFile,"cron_log.txt");
unlink($LCKFile);

function _log($LCKFile,$msg){
	$fp = fopen($LCKFile, "a");
	fwrite($fp, "[".date("d-m-Y в H:i:s")."] ".$msg."\n");
	fclose($fp);
}
?>