<?php

/**
 * main file action class
 */
class fileAction
{

    public $errorMsg = null;

    /**
     * Queue file to be deleted.
     * 
     * @param integer $serverId
     * @param string $filePath
     * @param integer $fileId
     * @param string $actionDate
     * @return boolean|\DBObject
     */
    static function queueDeleteFile($serverId, $filePath, $fileId = null, $actionDate = null)
    {
        // if no action date passed, assume it needs done straight away
        if ($actionDate == null)
        {
            $actionDate = coreFunctions::sqlDateTime();
        }

        $dbInsert = new DBObject("file_action", array("file_id", "server_id", "file_path", "file_action", "status", "date_created", "action_date"));

        $dbInsert->file_id      = $fileId;
        $dbInsert->server_id    = $serverId;
        $dbInsert->file_path    = $filePath;
        $dbInsert->file_action  = 'delete';
        $dbInsert->status       = 'pending';
        $dbInsert->date_created = coreFunctions::sqlDateTime();
        $dbInsert->action_date  = $actionDate;
        if (!$dbInsert->insert())
        {
            return false;
        }

        return $dbInsert;
    }

    static function processQueue($limitType = null, $limitActions = 100)
    {
        // get file servers
        $fileServers = file::getFileServerData();

        $currentServerDetails = file::getCurrentServerDetails();

        // next queue removal process, run every 60 seconds
        //if ($currentServerDetails['lastFileActionQueueProcess'] < (time() - (60)))
        if(1==1)
        {
            // get database
            $db = Database::getDatabase();

            // load pending queue items
            $pendingItems = $db->getRows('SELECT file_action.id, file_action.action_data, file_action.file_path, file_action.file_action, file_action.file_id, file.localFilePath, file.fileHash, file.serverId FROM file_action LEFT JOIN file ON file_action.file_id = file.id WHERE file_action.server_id = ' . (int) $currentServerDetails['id'] . ' AND file_action.status = \'pending\' AND file_action.action_date < NOW() '.($limitType!=null?('AND file_action.file_action = '.$db->quote($limitType)):'').'ORDER BY file_action.id ASC LIMIT '.$limitActions);
            if ($pendingItems)
            {
                foreach ($pendingItems AS $pendingItem)
                {
                    // reload item to make sure it's not been triggered by another instance of this script running and it's still pending
                    $checkPending = $db->getValue('SELECT id FROM file_action WHERE id='.(int)$pendingItem['id'].' AND status=\'pending\' LIMIT 1');
                    if(!$checkPending)
                    {
                        continue;
                    }
                    
                    // prepare file path
                    $filePath = trim($pendingItem['file_path']);

                    // validation
                    if (strlen($filePath) <= 1)
                    {
                        $db->query('UPDATE file_action SET status = \'failed\', status_msg=\'File has zero length or is in the root folder.\', last_updated=NOW() WHERE id = ' . (int) $pendingItem['id'] . ' LIMIT 1');
                        continue;
                    }

                    // make sure file exists
                    if (!file_exists($filePath))
                    {
                        $db->query('UPDATE file_action SET status = \'failed\', status_msg=\'File or folder does not exist.\', last_updated=NOW() WHERE id = ' . (int) $pendingItem['id'] . ' LIMIT 1');
                        continue;
                    }
                    
                    // set processing
                    $db->query('UPDATE file_action SET status = \'processing\', last_updated=NOW() WHERE id = ' . (int) $pendingItem['id'] . ' LIMIT 1');

                    // do action
                    switch($pendingItem['file_action'])
                    {
                        case 'delete':
                            // delete file or folder, this is the only option in this function for now
                            if (is_dir($filePath))
                            {
                                // directory
                                $rs = rmdir($filePath);
                            }
                            else
                            {
                                // file
                                $rs = unlink($filePath);
                            }

                            if ($rs === false)
                            {
                                $db->query('UPDATE file_action SET status = \'failed\', status_msg=\'Could not delete file or folder, possible permissions issue or folder has contents.\', last_updated=NOW() WHERE id = ' . (int) $pendingItem['id'] . ' LIMIT 1');
                                continue;
                            }
                            break;
                        case 'move':
                            $actionDetails = json_decode($pendingItem['action_data'], true);
                            $params = array();
                            $file_id = (int)$pendingItem['file_id'];
                            $current_server_id = (int)$pendingItem['serverId'];
                            $new_server_id = (int)$actionDetails['newServerId'];
                            
                            // load server details
                            $currentServer = file::loadServerDetails($current_server_id);
                            $newServer = file::loadServerDetails($new_server_id);

                            // attempt file move via scp using phpseclib
                            set_include_path(CORE_ROOT . '/includes/phpseclib');
                            include('Net/SSH2.php');
                            
                            $fromServerHost = 'host';
                            $fromServerUser = 'user';
                            $fromServerPass = 'password';
                            $fromFilePath = '/var/www/php_site_scripts/file_hosting/files/_deleted/4f/4f190a6e7b396e160b7b99bdbd90f617';
                            $remoteServerHost = 'host';
                            $remoteServerUser = 'user';
                            $remoteServerPass = 'password';
                            $toFilePath = '/usr/share/nginx/www/files/2b/';
                            $toFileFullPath = '/usr/share/nginx/www/files/2b/'.basename($fromFilePath);
                            $sudo = 'sudo ';
                            $moveFileCommand = $sudo.'scp -oStrictHostKeyChecking=no '.$fromFilePath.' '.$remoteServerUser.'@'.$remoteServerHost.':'.$toFilePath;
                            
                            $ssh = new Net_SSH2($fromServerHost);
                            $ssh->enablePTY();
                            $ssh->setTimeout(10);
                            if (!$ssh->login($fromServerUser, $fromServerPass))
                            {
                                log::error('Login failed on '.$fromServerHost);
                                $db->query('UPDATE file_action SET status = \'failed\', status_msg=\'Could not move file.\', last_updated=NOW() WHERE id = '.(int)$pendingItem['id'].' LIMIT 1');
                                continue;
                            }

                            $ssh->read('username@username:~$');
                            $ssh->write($moveFileCommand."\n");
                            $output = $ssh->read('#[pP]assword[^:]*:|username@username:~\$#', NET_SSH2_READ_REGEX);
                            
                            // sudo required
                            if (preg_match('/.*[sudo].*/', $output))
                            {
                                $ssh->write($fromServerPass."\n");
                                $output = $ssh->read('username@username:~$#');
                            }

                            // scp remote password
                            if (preg_match('#[pP]assword[^:]*:#', $output))
                            {
                                $ssh->write($fromServerPass."\n");
                                $output = $ssh->read('username@username:~#');
                            }
                            
                            // sub folder does not exist
                            $ssh2 = null;
                            if(strpos(strtolower($output), 'is a directory'))
                            {
                                // try to create the sub folder and retry transfer
                                $ssh2 = new Net_SSH2($remoteServerHost);
                                $ssh2->enablePTY();
                                $ssh2->setTimeout(10);
                                if (!$ssh2->login($remoteServerUser, $remoteServerPass))
                                {
                                    log::error('Login failed on '.$remoteServerHost);
                                    $db->query('UPDATE file_action SET status = \'failed\', status_msg=\'Could not move file.\', last_updated=NOW() WHERE id = '.(int)$pendingItem['id'].' LIMIT 1');
                                    continue;
                                }
                                
                                // set permissions
                                $ssh2->read('username@username:~$');
                                $ssh2->write($sudo.'mkdir '.$toFilePath."\n");
                                $output = $ssh2->read('#[pP]assword[^:]*:|username@username:~\$#', NET_SSH2_READ_REGEX);
                                if (preg_match('/.*[sudo].*/', $output))
                                {
                                    $ssh2->write($remoteServerPass."\n");
                                    $output = $ssh2->read('username@username:~$#');
                                }
                                
                                // retry moving file
                                $ssh->write($moveFileCommand."\n");
                                $output = $ssh->read('#[pP]assword[^:]*:|username@username:~\$#', NET_SSH2_READ_REGEX);
                                
                                // scp remote password
                                if (preg_match('#[pP]assword[^:]*:#', $output))
                                {
                                    $ssh->write($fromServerPass."\n");
                                    $output = $ssh->read('username@username:~#');
                                }
                            }
                            
                            // permission denied on sub folder
                            if(strpos(strtolower($output), 'permission denied'))
                            {
                                // try to set permissions on folder and retry transfer
                                if($ssh2 === null)
                                {
                                    $ssh2 = new Net_SSH2($remoteServerHost);
                                    $ssh2->enablePTY();
                                    $ssh2->setTimeout(10);
                                    if (!$ssh2->login($remoteServerUser, $remoteServerPass))
                                    {
                                        log::error('Login failed on '.$remoteServerHost);
                                        $db->query('UPDATE file_action SET status = \'failed\', status_msg=\'Could not move file.\', last_updated=NOW() WHERE id = '.(int)$pendingItem['id'].' LIMIT 1');
                                        continue;
                                    }
                                }
                                
                                // set permissions
                                $ssh2->read('username@username:~$');
                                $ssh2->write($sudo.'chmod 777 '.$toFilePath."\n");
                                $output = $ssh2->read('#[pP]assword[^:]*:|username@username:~\$#', NET_SSH2_READ_REGEX);
                                if (preg_match('/.*[sudo].*/', $output))
                                {
                                    $ssh2->write($remoteServerPass."\n");
                                    $output = $ssh2->read('username@username:~$#');
                                }
                                
                                // retry moving file
                                $ssh->write($moveFileCommand."\n");
                                $output = $ssh->read('#[pP]assword[^:]*:|username@username:~\$#', NET_SSH2_READ_REGEX);
                                
                                // scp remote password
                                if (preg_match('#[pP]assword[^:]*:#', $output))
                                {
                                    $ssh->write($fromServerPass."\n");
                                    $output = $ssh->read('username@username:~#');
                                }
                            }
                            
                            // original file not found
                            if(strpos(strtolower($output), 'file not found'))
                            {
                                // log error
                                log::error("File to transfer not found: ".$output);
                                $db->query('UPDATE file_action SET status = \'failed\', status_msg=\'Could not move file.\', last_updated=NOW() WHERE id = '.(int)$pendingItem['id'].' LIMIT 1');
                                continue;
                            }
                            
                            // look for 100% in the response to assume sucessful transfer
                            if(strpos($output, '100%') === false)
                            {
                                log::error("Failed transfer, last response: ".$output);
                                $db->query('UPDATE file_action SET status = \'failed\', status_msg=\'Could not move file.\', last_updated=NOW() WHERE id = '.(int)$pendingItem['id'].' LIMIT 1');
                                continue;
                            }

                            // double check file exists on new server
                            if($ssh2 === null)
                            {
                                $ssh2 = new Net_SSH2($remoteServerHost);
                                $ssh2->enablePTY();
                                $ssh2->setTimeout(10);
                                if (!$ssh2->login($remoteServerUser, $remoteServerPass))
                                {
                                    log::error('Login failed on '.$remoteServerHost);
                                    $db->query('UPDATE file_action SET status = \'failed\', status_msg=\'Could not move file.\', last_updated=NOW() WHERE id = '.(int)$pendingItem['id'].' LIMIT 1');
                                    continue;
                                }
                            }
                            $ssh2->read('username@username:~$');
                            $ssh2->write($sudo.'ls -l '.$toFileFullPath."\n");
                            $sizeCheck = $ssh2->read('#[pP]assword[^:]*:|username@username:~\$#', NET_SSH2_READ_REGEX);
                            if (preg_match('/.*[sudo].*/', $sizeCheck))
                            {
                                $ssh2->write($remoteServerPass."\n");
                                $sizeCheck = $ssh2->read('username@username:~$#');
                            }
                            
                            if(strpos(strtolower($output), 'no such file'))
                            {
                                log::error("Failed transfer when double checking for the file on remote server, last response: ".$output);
                                $db->query('UPDATE file_action SET status = \'failed\', status_msg=\'Could not move file.\', last_updated=NOW() WHERE id = '.(int)$pendingItem['id'].' LIMIT 1');
                                continue;
                            }

                            // file has been successfully moved, remove from the original server
                            $ssh->read('username@username:~$');
                            $ssh->write($sudo.'rm '.$fromFilePath."\n");
                            $deleteFile = $ssh->read('#[pP]assword[^:]*:|username@username:~\$#', NET_SSH2_READ_REGEX);
                            if (preg_match('/.*[sudo].*/', $deleteFile))
                            {
                                $ssh->write($fromServerPass."\n");
                                $deleteFile = $ssh->read('username@username:~$#');
                            }
                            
                            // complete any pending in the file_action table which are the same file
                            $db->query('UPDATE file_action SET status = \'complete\', last_updated=NOW() WHERE file_id IN (SELECT id FROM file WHERE serverId = '.(int)$current_server_id.' AND localFilePath = '.$db->quote($pendingItem['localFilePath']).')');
                            
                            // update database to point at new file
                            $currentHash = $db->query('UPDATE file SET serverId = x WHERE serverId = '.(int)$current_server_id.' AND localFilePath = '.$db->quote($pendingItem['localFilePath']));
                            break;
                    }

                    // update file record
                    $db->query('UPDATE file_action SET status = \'complete\', last_updated=NOW() WHERE id = ' . (int) $pendingItem['id'] . ' LIMIT 1');
                    continue;
                }
            }

            // update next run
            $db->query('UPDATE file_server SET lastFileActionQueueProcess = ' . $db->quote(time()) . ' WHERE id = ' . (int) $currentServerDetails['id'] . ' LIMIT 1');
        }
    }

    /**
     * Queue file to be moved.
     * 
     * @param type $serverId
     * @param type $filePath
     * @param type $fileId
     * @param type $newServerId
     * @return boolean|\DBObject
     */
    static function queueMoveFile($serverId, $filePath, $fileId, $newServerId)
    {
        // if no action date passed, assume it needs done straight away
        if ($actionDate == null)
        {
            $actionDate = coreFunctions::sqlDateTime();
        }

        $dbInsert = new DBObject("file_action", array("file_id", "server_id", "file_path", "file_action", "action_data", "status", "date_created", "action_date"));

        $dbInsert->file_id      = $fileId;
        $dbInsert->server_id    = $serverId;
        $dbInsert->file_path    = $filePath;
        $dbInsert->file_action  = 'move';
        $dbInsert->action_data  = json_encode(array('newServerId' => $newServerId));
        $dbInsert->status       = 'pending';
        $dbInsert->date_created = coreFunctions::sqlDateTime();
        $dbInsert->action_date  = $actionDate;
        if (!$dbInsert->insert())
        {
            return false;
        }

        return $dbInsert;
    }

    static function createSystemConfigTrackers()
    {
        // get file servers
        $fileServers = file::getFileServerData();
    }

}
